viprs(1) CLI Documentation
Name
viprs — generate tile pyramids from images and PDFs for web-based viewers
Synopsis
viprs pyramid [OPTIONS] <INPUT> [<OUTPUT>]
viprs info <INPUT>
viprs plan [OPTIONS] <WIDTH_OR_INPUT>
viprs test-image [OPTIONS] <OUTPUT>
# Phase 3 hardening flags (subset)
viprs pyramid input.pdf tiles/ --blank-tolerance 2
viprs pyramid input.pdf --sink s3://bucket/prefix
viprs pyramid input.pdf out/ --resume --retry-max 3 --retry-backoff 200
viprs pyramid input.pdf out/ --manifest-emit-checksums --checksum-algo blake3
viprs pyramid input.pdf out/ --dedupe-blanks
viprs pyramid input.pdf --sink packfile://out.tar.gz
viprs pyramid input.pdf out/ --trace-level info
Description
viprs converts large raster images and multi-page PDF documents — high-resolution scans, gigapixel photographs, aerial and satellite imagery, historical maps, and digitised archives — into multi-resolution tile pyramids suitable for Deep Zoom viewers, slippy-map UIs, and GIS applications. It supports three tile layouts (DeepZoom, XYZ, Google Maps), three image formats (PNG, JPEG, raw), and three engine modes (monolithic, streaming, parallel MapReduce) to balance throughput and memory usage for images of any size.
The XYZ and Deep Zoom tile pyramids it emits are served directly to and rendered by MapLibre GL — the open-source WebGL map and tile engine — so a gigapixel source can be panned and zoomed at interactive speed in the browser.
Commands
pyramid
Generate a tile pyramid from a PDF or image file. This is the primary command — it loads the source, plans the pyramid grid, and writes tiles to disk.
The Rust program below is what viprs pyramid input.pdf tiles/ runs internally with no flags. Click any flag below to see how it slots into this template, or check flags to assemble a complete program in the generator panel at the bottom.
// Pre-generated; the live version reflects flag selections.
Reference test: builder_sink_fs.rs · two_arg_new_defaults_to_png — the canonical minimal builder call.
Arguments
-
<INPUT> -
Path to a PDF, PNG, JPEG, or TIFF file. Use
"-"for stdin (pipe a file directly into viprs). PDF inputs are either raster-extracted (default) or vector-rendered (with--render). -
<OUTPUT> -
Directory where tiles are written. Created automatically. DeepZoom also writes a
.dzimanifest alongside.
Tile options
--tile-size <PIXELS>-
Width and height of each tile in pixels. Viewers fetch tiles on demand — smaller tiles mean finer-grained loading but more HTTP requests. 256 is the web standard; use 512 for high-DPI displays or when serving from fast storage. Default: 256
--overlap <PIXELS>-
Pixel overlap between adjacent tiles (DeepZoom only). Overlap lets viewers blend tile edges for seamless rendering. OpenSeadragon typically uses overlap=1. Not applicable to Google or XYZ layouts. Default: 0
--layout <FORMAT>-
Tile naming and directory convention. Different viewers expect different conventions — OpenSeadragon uses DeepZoom, Leaflet uses XYZ, Google Maps uses its own grid. Default: deep-zoom
Values:
deep-zoom({level}/{col}_{row}.ext+.dzi),xyz({z}/{x}/{y}.ext),google({z}/{y}/{x}.ext, power-of-2 grids). --format <TYPE>-
Tile image encoding. PNG preserves quality for line art and text; JPEG saves disk for photographs; raw is useful when a downstream pipeline handles encoding. Default: png
Values:
png(lossless),jpeg(lossy, smaller),raw(unencoded pixels, fastest). --quality <1-100>-
JPEG quality factor. Only used with
--format jpeg. Default: 85 --centre-
Centre the image within the tile grid, adding even background padding on all sides. Google Maps layout requires power-of-2 tile grids — without centring, the image sits at (0,0) and padding fills the right/bottom. With centring, padding is distributed evenly so the image appears centred in the viewer.
--skip-blank-
Replace blank (uniform-color) tiles with a 1-byte placeholder instead of encoding the full image. Scanned documents have large white margins that produce many identical tiles — placeholder files reduce output size dramatically. Consumers detect them by file size and render a solid color.
--blank-tolerance <N>-
Treat a tile as blank when every channel in every pixel varies by at most
Nfrom the first pixel's value. Extends--skip-blankto near-uniform tiles; useful for slightly noisy scanner output where pure-white detection misses off-white margins. Requires--skip-blankor--dedupe-blanks. Default: 0 (exact match only)
PDF options
--dpi <N>-
Resolution for PDF rendering and page-size scaling. Higher DPI produces more pixels (and more tiles). 72 matches libvips' default; use 150–300 for print-quality scans. Default: 72
--page <N>-
PDF page to extract (1-based). Default: 1
--render-
Use PDFium for vector PDF rendering instead of extracting embedded raster images. Scanned documents embed a raster image inside the PDF — extraction is fast and lossless. Vector-authored PDFs and text-heavy documents contain vector paths that must be rendered to pixels. Use
--renderwhen the PDF is not a simple raster scan. --match-page-size-
After extracting a raster from a PDF, resize it to match the PDF page dimensions at the specified
--dpi. Has no effect with--render. Embedded rasters may have a different resolution than the page's declared dimensions — this flag produces output consistent with libvips' default PDF handling.
Engine options
--concurrency <N>-
Number of worker threads for tile extraction. Tile extraction is embarrassingly parallel — each tile reads from the source independently. Set to the number of CPU cores for maximum throughput. With
--parallel, this controls per-strip tile workers. Default: 0 (single-threaded) --buffer-size <N>-
Maximum tiles buffered between producer threads and the sink. A large buffer smooths out slow disk writes at the cost of higher memory; a small buffer limits peak memory but may cause producers to block. Default: 64
--memory-limit <MB>-
Hard memory limit in MB. If the estimated peak memory exceeds this, the command exits before rendering. Acts as a safety net for containerised deployments — prevents the engine from attempting a pyramid that would OOM-kill the process. Combine with
--memory-budgetto both limit and control memory. Default: 0 (disabled) --memory-budget <MB>-
Soft memory budget for strip-based processing. Switches from the monolithic engine to the streaming engine, which processes the image in horizontal bands. A 16820×11888 scan at 300 DPI needs ~5 GB as a monolithic canvas — the streaming engine reduces this to ~50 MB by processing strips. The strip height is maximised within the budget to balance memory and speed.
When set to
0: auto-selects a budget (1/4 of estimated monolithic peak). When omitted: uses the monolithic engine (original behavior). --parallel-
Use the parallel MapReduce engine instead of the sequential streaming engine. Requires
--memory-budget. On multi-core systems, the MapReduce engine overlaps strip rendering for higher throughput — the sequential streaming engine processes one strip at a time. Both produce byte-identical output.Renders multiple strips concurrently (bounded by the budget), then propagates downscale results sequentially. The
--concurrencyflag controls per-strip tile worker threads.
Output sink options
--sink <URI>-
Output sink URI. The default filesystem sink is used when this flag is omitted (the
<OUTPUT>positional argument sets the directory). Two additional sink types are available with optional features:s3://bucket/prefix— write tiles to an S3-compatible object store. Requires--features s3at compile time and standard AWS credentials in the environment.packfile://out.tar,packfile://out.tar.gz,packfile://out.zip— archive the pyramid into a single file. Requires--features packfile.
Resumable job options
--resume-
Resume an interrupted run. Reads the checkpoint file (
.libviprs-job.json) at the output root and skips tiles that were already written. The run completes from the last safe checkpoint. Cannot be combined with--overwriteor--verify. --overwrite-
Start fresh even if a previous run's output or checkpoint exists. All existing tiles and the checkpoint file are removed before the job begins. Default behaviour when none of
--resume/--overwrite/--verifyis given --verify-
Re-read every tile produced by a previous run and verify its checksum against the checkpoint. Exits with a non-zero status if any tile is missing or corrupt. Does not write new tiles. Requires checksums to have been emitted during the original run (
--manifest-emit-checksums).
Retry and failure policy options
--retry-max <N>-
Maximum number of attempts for a single tile write before the failure policy is applied. Default: 0 (no retry)
--retry-backoff <MS>-
Initial back-off delay in milliseconds between retry attempts. Each subsequent attempt doubles the delay (exponential back-off). Default: 100
--failure-policy <POLICY>-
What to do when a tile write exhausts its retries. Default: fail-fast
Values:
fail-fast(abort immediately),retry-then-fail(retry up to--retry-maxtimes then abort),retry-then-skip(retry then record the tile as missing in the manifest and continue).
Checksum options
--manifest-emit-checksums-
Compute a digest for each tile and embed it in the manifest JSON. Does not verify existing tiles; use
--verifyfor that. Requires--features checksum. --checksum-algo <ALGO>-
Hash algorithm for tile digests. Only used with
--manifest-emit-checksumsor--verify. Default: blake3Values:
blake3(faster, 256-bit),sha256(wider tooling support).
Deduplication options
--dedupe-blanks-
Store only one copy of the blank placeholder tile and point all other blank positions at it via a symlink or hardlink. Significantly reduces inode count on images with large uniform margins. Requires
--features dedupe. --dedupe-all-
Extend deduplication to every tile, not just blanks. The engine hashes each encoded tile; duplicate content is stored once and the rest are links or manifest references. Implies
--manifest-emit-checksums. Requires--features dedupe. Slower than--dedupe-blanksdue to per-tile hashing.
Tracing options
--trace-level <LEVEL>-
Emit structured tracing spans at the given verbosity level. Spans are named
libviprs::pipeline,libviprs::level, andlibviprs::tile. Attach anytracing-compatible subscriber (e.g.tracing-opentelemetry) to export spans to Jaeger, Tempo, or another collector. Requires--features tracingat compile time. Default: offValues:
error,warn,info,debug,trace.
Geo-reference options
--geo-origin <LON,LAT>-
Geographic coordinate of the top-left pixel as
"longitude,latitude". --geo-scale <SX,SY>-
Degrees per pixel as
"scale_x,scale_y". Typically positive X (east) and negative Y (south). Together with--geo-origin, defines an affine transform that lets tile viewers overlay the pyramid on a world map.
Generated CLI & Rust code
No flags selected. Check boxes above to assemble a CLI command and a complete Rust program.
CLI command
Equivalent Rust program
Each flag links to a representative integration test in libviprs-tests that exercises the underlying Rust API. Edit the input/output paths in the generated program before running.
info
Show metadata about a PDF or image file without generating tiles. For PDFs, displays page count, dimensions, and embedded image details. For images, displays pixel dimensions and format.
use libviprs::{pdf_info, decode_file};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let path = Path::new("/path/to/your/input.pdf");
if path.extension().and_then(|e| e.to_str()) == Some("pdf") {
let info = pdf_info(path)?;
println!("PDF: {} pages", info.page_count);
for page in &info.pages {
println!(
" Page {}: {:.1} x {:.1} pts",
page.page_number, page.width_pts, page.height_pts,
);
}
} else {
let raster = decode_file(path)?;
println!(
"{}x{} {:?}",
raster.width(), raster.height(), raster.format(),
);
}
Ok(())
}
Reference tests:
pdf_ops.rs · pdf_info_reads_page_count
·
libviprs/src/source.rs · generate_test_raster_dimensions (decode flow).
<INPUT>-
Path to the PDF or image file to inspect. Useful before running
pyramidto understand source dimensions, plan memory usage, and decide on DPI settings.
plan
Show the tile pyramid plan without generating any tiles. Displays the number of levels, total tiles, and per-level grid dimensions. Useful for capacity planning and understanding how tile parameters affect output.
use libviprs::{Layout, PyramidPlanner};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Plan from explicit dimensions — no source file needed.
let planner = PyramidPlanner::new(
16820, 11888, // width, height
256, // --tile-size
0, // --overlap
Layout::Google, // --layout
)?
.with_centre(true); // --centre
let plan = planner.plan();
let (canvas_w, canvas_h) = planner.canvas_dimensions();
println!("Canvas: {}x{}", canvas_w, canvas_h);
println!("Levels: {}, total tiles: {}",
plan.level_count(),
plan.total_tile_count(),
);
println!("Estimated peak memory: {:.1} MB",
planner.estimate_peak_memory() as f64 / (1024.0 * 1024.0));
Ok(())
}
Reference tests:
libviprs/src/planner.rs · total_tile_count_sums_all_levels
·
streaming_engine.rs · estimate_streaming_memory_reasonable (peak-memory estimation).
<WIDTH_OR_INPUT>-
Either a pixel width (when paired with
--height) or a path to an image/PDF file to read dimensions from. --height <PIXELS>Image height in pixels. Required when the first argument is a number.
--tile-size,--overlap,--layout,--centreSame as in
pyramid. Control how the plan is computed.--dpi,--pageUsed when the input is a PDF to resolve page dimensions to pixels.
test-image
Generate a synthetic RGB8 gradient test image. Useful for benchmarking, testing, and verifying the tile pipeline without needing real image data.
use libviprs::{generate_test_raster, sink::encode_png};
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let raster = generate_test_raster(1024, 1024)?;
let bytes = encode_png(&raster)?;
fs::write("/path/to/output.png", bytes)?;
Ok(())
}
Reference tests:
libviprs/src/source.rs · generate_test_raster_dimensions
·
libviprs/src/sink.rs · encode_png_gray8.
<OUTPUT>Output file path (PNG).
--width <PIXELS>Image width. Default: 1024
--height <PIXELS>Image height. Default: 1024
abs
Absolute value of every sample.
viprs abs in.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_abs(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
add
Add two images pixel-by-pixel (8-bit widens to 16-bit to avoid wrap).
viprs add left.png right.png out.v --max-width PX --max-height PX
let left = io::load(&left_path, &limits)?;
let right = io::load(&right_path, &limits)?;
// Guard the three conditions core's `add` asserts on, so a bad input is exit
// 1 (a clear message) and NEVER a process abort (exit 101).
if left.format().is_float() || right.format().is_float() {
bail!(
"add: float inputs are not supported yet (a later arithmetic batch adds float \
addition); cast to an unsigned 8-bit format first"
);
}
// Reject 16-bit (and any non-uchar) inputs: vips `add` promotes ushort→uint
// and returns the true sum, but core keeps 16-bit at 16-bit and SATURATES at
// 65535 (raster_ops.rs). Accepting them would emit a silently-wrong result,
// so the accepted surface is restricted to 8-bit (uchar), where core == vips.
// Wide-input addition lands with a later arithmetic batch (see the module
// docs / OP_MAP `add` note).
if left.format().bytes_per_channel() != 1 || right.format().bytes_per_channel() != 1 {
bail!(
"add: 16-bit inputs are not supported yet (a later arithmetic batch adds \
ushort→uint promotion; core would saturate the sum at 65535 instead of \
promoting like vips); cast to an unsigned 8-bit format first"
);
}
if (left.width(), left.height()) != (right.width(), right.height()) {
bail!(
"add: dimension mismatch — LEFT is {}x{} but RIGHT is {}x{}",
left.width(),
left.height(),
right.width(),
right.height()
);
}
if left.format().channels() != right.format().channels() {
bail!(
"add: channel-count mismatch — LEFT has {} band(s) but RIGHT has {}",
left.format().channels(),
right.format().channels()
);
}
let out = left.add(&right);
io::save(&out, &out_path)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
addalpha
Append a fully-opaque alpha channel to an image.
viprs addalpha in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_addalpha()?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image with an added alpha band Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
affine
Apply a 2x2 affine matrix transform to an image.
viprs affine in.png out.v matrix.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_affine(matrix, interpolate)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<MATRIX>2x2 transform matrix as one space-separated string, e.g. "1.5 0 0 1.5" Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
--interpolate<I>Point interpolator (nearest|bilinear|bicubic|nohalo|lbb) One of:
nearest,bilinear,bicubic,nohalo,lbb. Default: bilinear
arrayjoin
Tile an array of images into a grid.
viprs arrayjoin inputs.png --across N --shim 0
let rasters: Vec<Raster> = inputs
.iter()
.map(|p| io::load(p, &limits))
.collect::<Result<_>>()?;
let refs: Vec<&Raster> = rasters.iter().collect();
let out = Raster::try_arrayjoin(&refs, across, shim)?;
io::save(&out, &out_path)?;
<INPUTS>Two or more input images followed by the output path Required.
--across<N>Number of images across the grid (>= 1; default all in one row)
--shim<PX>Pixels of black gap between cells (default 0) Default: 0
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
autorot
Auto-rotate an image by its EXIF orientation tag.
viprs autorot in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_autorot()?;
io::save(&out, &out_path)?;
<IN>Input image (orientation from TIFF/.v metadata) Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
avg
Find the mean of every sample in an image and print it.
viprs avg in.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let value = raster.try_avg(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
println!("{value}");
<IN>Input image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
bandbool
Fold an image's bands with a bitwise boolean operation.
viprs bandbool in.png out.v op.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = match op {
"and" => raster.try_bandand()?,
"or" => raster.try_bandor()?,
"eor" => raster.try_bandeor()?,
other => bail!("unknown bandbool operation {other:?} (expected and|or|eor)"),
};
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<OP>Bitwise operation to fold the bands with (and|or|eor; vips's lshift/rshift are intentionally excluded — not core-backed) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
bandfold
Fold the x axis into bands.
viprs bandfold in.png out.v --factor 0 --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_bandfold(factor)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
--factor<N>Fold by this factor (0 = fold the whole width into bands) Default: 0
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
bandjoin
Join the bands of two or more images into one image.
viprs bandjoin inputs.png --max-width PX --max-height PX
let mut acc = io::load(&inputs[0], &limits)?;
for path in &inputs[1..] {
let next = io::load(path, &limits)?;
acc = acc.try_bandjoin(&next)?;
}
io::save(&acc, &out_path)?;
<INPUTS>Two or more input images followed by the output path Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
bandjoin_const
Append one constant band per element of a vector to an image.
viprs bandjoin_const in.png out.v c.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_bandjoin_vec(&consts)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<C>Space-separated constants, one appended band each (e.g. "10 20") Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
bandmean
Average an image's bands into a single-band image.
viprs bandmean in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_bandmean()?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
bandrank
Select a per-sample rank statistic across a set of images.
viprs bandrank inputs.png --index -1 --max-width PX
let rasters: Vec<Raster> = inputs
.iter()
.map(|p| io::load(p, &limits))
.collect::<Result<_>>()?;
let (first, rest) = rasters
.split_first()
.expect("inputs_and_out guarantees at least one input");
let others: Vec<&Raster> = rest.iter().collect();
let out = first.try_bandrank(&others, index)?;
io::save(&out, &out_path)?;
<INPUTS>Two or more input images followed by the output path Required.
--index<I>Sorted-list index to select (>= -1: 0 = min, count-1 = max, -1 = median, the default) Default: -1
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
bandunfold
Unfold image bands into the x axis.
viprs bandunfold in.png out.v --factor 0 --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_bandunfold(factor)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
--factor<N>Unfold by this factor (0 = unfold every band into the width) Default: 0
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
black
Make an all-zero uchar image.
viprs black out.v width.png height.png --bands 1
let out_raster = Raster::try_black_bands(dim(m, "WIDTH"), dim(m, "HEIGHT"), bands)?;
io::save(&out_raster, &out_path(m))?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
--bands<N>Number of bands (vips --bands, default 1) Default: 1
boolean
Samplewise bitwise boolean operation on two images.
viprs boolean left.png right.png out.v op.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_boolean(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output image Required.
<OP>Boolean operator (and|or|eor). vips's lshift|rshift are 2-image in vips but core-only as boolean_const constants, so they are NOT accepted here — use boolean_const. Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
boolean_const
Samplewise bitwise boolean operation against a constant.
viprs boolean_const in.png out.v op.png c.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_boolean_const(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<OP>Boolean operator to apply against the constant Required.
<C>Integer constant (bit-mask for and|or|eor, shift count for lshift|rshift; a shift must be >= 0). NOTE: vips takes a per-band constant vector; core applies ONE constant to all bands, so a single scalar is accepted here. Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
buildlut
Build a look-up table from a control-point matrix file.
viprs buildlut in.png out.v
let raster = decode_file(&input)?;
let result = raster.try_buildlut()?;
save_file(&result, &output)?;
<IN>Control-point matrix as a vips text matrix file Required.
<OUT>Output image path Required.
byteswap
Swap the byte order within every sample.
viprs byteswap in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
// `byteswap` is infallible (it can never fail on any input): a straight
// per-sample byte reversal, so there is no `try_byteswap` to call.
let out = raster.byteswap();
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
cast
Cast an image to a new pixel format (uchar, ushort, or float).
viprs cast in.png out.v format.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
// The target keeps the input's band count (try_cast rejects a band-count
// change); only the per-channel byte width changes.
let channels = raster.format().channels();
let bytes = match fmt_name {
"uchar" => 1,
"ushort" => 2,
"float" => 4,
other => bail!("unsupported cast format {other:?} (expected uchar|ushort|float)"),
};
let target = PixelFormat::with_channels(channels, bytes)
.ok_or_else(|| anyhow!("no {fmt_name} format exists for a {channels}-band image"))?;
let out = raster.try_cast(target)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<FORMAT>Target format: uchar (8-bit), ushort (16-bit), or float (f32). char/short/int/uint/complex/double/dpcomplex are not core-backed (#283/#285). Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
clamp
Clip every sample into [min, max] (vips defaults 0 and 1).
viprs clamp in.png out.v --min V --max V
let raster = decode_file(&input)?;
let result = raster.try_clamp(min, max, max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
--min<V>Lower bound (default 0)
--max<V>Upper bound (default 1)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
colourspace
Convert an image to a new colour space.
viprs colourspace in.png out.v space.png --source-space multiband --max-width PX
let raster = io::load(&in_path, &limits)?;
// `--source-space` re-tags the input's interpretation (the core reads the
// source space from the raster's tag) before the conversion; absent, the
// input's own interpretation is used, mirroring vips.
let input = match source {
Some(space) => raster.copy().interpretation(space).build(),
None => raster,
};
let out = input.try_colourspace(target)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<SPACE>Destination colour space (vips VipsInterpretation nickname) Required.
--source-space<space>Source colour space; overrides the input's own interpretation tag (default: the input's interpretation) One of:
multiband,b-w,histogram,xyz,lab,cmyk,labq,rgb,cmc,lch,labs,srgb,yxy,fourier,rgb16,grey16,matrix,scrgb,hsv,oklab,oklch.--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
compass
Convolve with a rotating compass mask and combine the results.
viprs compass in.png out.v mask.png --times 2 --angle d0
let raster = io::load(&in_path, &limits)?;
let out = raster.try_compass(&kernel, times, angle, combine, precision)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<MASK>Odd-sided square mask as a vips text matrix Required.
--times<N>Rotate and convolve this many times (1..=1000) Default: 2
--angle<d0|d45|…|d315>Rotate the mask by this much between convolutions One of:
d0,d45,d90,d135,d180,d225,d270,d315. Default: d90--combine<max|sum>Combine the results (max|sum; vips's min is not core-backed) One of:
max,sum. Default: max--precision<integer|float>Calculation precision (integer|float; vips's approximate is not core-backed) One of:
integer,float. Default: float--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
complex
Apply a complex operation to a complex (re,im band-pair) image.
viprs complex in.png out.v op.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_complex(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input complex image ((re,im) band pairs, use .v) Required.
<OUT>Output complex image (use .v) Required.
<OP>Complex operation (polar/rect angles in DEGREES) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
complexform
Form a complex image from two real images (left=real, right=imag).
viprs complexform left.png right.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_complexform(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Real-part input image Required.
<RIGHT>Imaginary-part input image Required.
<OUT>Output complex image as (re,im) band pairs (use .v) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
complexget
Extract the real or imaginary component from a complex image.
viprs complexget in.png out.v op.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_complexget(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input complex image ((re,im) band pairs, use .v) Required.
<OUT>Output real image (float; use .v) Required.
<OP>Component to extract Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
composite
Blend two images with a blend mode (vips array form, two inputs).
viprs composite base.png overlay.png out.v mode.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_composite(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<BASE>Base image (the backdrop) Required.
<OVERLAY>Overlay image (the source), same size as BASE Required.
<OUT>Output image Required.
<MODE>Blend mode: one of the 25 vips VipsBlendMode names (clear|source|over|in|out|atop|dest|dest-over|dest-in|dest-out|dest-atop|xor|add|saturate|multiply|screen|overlay|darken|lighten|colour-dodge|colour-burn|hard-light|soft-light|difference|exclusion) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
composite2
Blend a base and an overlay image with a blend mode.
viprs composite2 base.png overlay.png out.v mode.png --max-width PX --max-height PX
let base = io::load(&base_path, &limits)?;
let overlay = io::load(&overlay_path, &limits)?;
let out = base.try_composite2(&overlay, mode)?;
io::save(&out, &out_path)?;
<BASE>Base image (the backdrop) Required.
<OVERLAY>Overlay image (the source), same size as BASE Required.
<OUT>Output image Required.
<MODE>Blend mode: one of the 25 vips VipsBlendMode names (clear|source|over|in|out|atop|dest|dest-over|dest-in|dest-out|dest-atop|xor|add|saturate|multiply|screen|overlay|darken|lighten|colour-dodge|colour-burn|hard-light|soft-light|difference|exclusion) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
conv
Convolve an image with a mask matrix.
viprs conv in.png out.v mask.png --precision integer --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_conv(&kernel, precision)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<MASK>Convolution mask as a vips text matrix (with an optional scale) Required.
--precision<integer|float>Calculation precision (integer|float; vips's approximate is not core-backed) One of:
integer,float. Default: float--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
convsep
Separably convolve an image with a 1xN or Nx1 mask.
viprs convsep in.png out.v mask.png --precision integer --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_convsep(&kernel, precision)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<MASK>Separable (1xN or Nx1) mask as a vips text matrix Required.
--precision<integer|float>Calculation precision (integer|float; vips's approximate is not core-backed) One of:
integer,float. Default: float--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
copy
Copy an image, tweaking header fields (interpretation, resolution, offset).
viprs copy in.png out.v --interpretation multiband --xres PPMM
let raster = io::load(&in_path, &limits)?;
let mut builder = raster.copy();
if let Some(s) = m.get_one::<String>("interpretation") {
builder = builder.interpretation(interpretation_from_str(s)?);
}
if let Some(&v) = m.get_one::<f64>("xres") {
builder = builder.xres(v);
}
if let Some(&v) = m.get_one::<f64>("yres") {
builder = builder.yres(v);
}
if let Some(&v) = m.get_one::<i32>("xoffset") {
builder = builder.xoffset(v);
}
if let Some(&v) = m.get_one::<i32>("yoffset") {
builder = builder.yoffset(v);
}
if let Some(&v) = m.get_one::<u8>("orientation") {
builder = builder.orientation(v);
}
let out = builder.build();
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
--interpretation<SPACE>Set the colour interpretation tag One of:
multiband,b-w,histogram,xyz,lab,cmyk,labq,rgb,cmc,lch,labs,srgb,yxy,fourier,rgb16,grey16,matrix,scrgb,hsv,oklab,oklch.--xres<PPMM>Horizontal resolution in pixels/mm
--yres<PPMM>Vertical resolution in pixels/mm
--xoffset<PX>Horizontal offset of the image origin
--yoffset<PX>Vertical offset of the image origin
--orientation<N>viprs-only (hidden, non-vips): set the EXIF-style orientation tag (1..=8)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
countlines
Count the average number of lines in a one-band image and print it.
viprs countlines in.png direction.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let nolines = raster.try_countlines(direction)?;
<IN>Input image (one band, unsigned 8/16-bit) Required.
<DIRECTION>Line direction to count Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
crop
Crop a rectangle out of an image (alias of extract_area).
viprs crop in.png out.v left.png top.png width.png height.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_crop(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<LEFT>Left edge of the extract area Required.
<TOP>Top edge of the extract area Required.
<WIDTH>Width of the extract area Required.
<HEIGHT>Height of the extract area Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
dE00
CIEDE2000 colour difference between two images.
viprs dE00 left.png right.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_dE00(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output difference image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
dE76
CIE76 colour difference between two images.
viprs dE76 left.png right.png out.v --max-width PX --max-height PX
let left = io::load(&left_path, &limits)?;
let right = io::load(&right_path, &limits)?;
let out = match kind {
DeKind::E76 => left.try_de76(&right)?,
DeKind::E00 => left.try_de00(&right)?,
DeKind::Cmc => left.try_de_cmc(&right)?,
};
io::save(&out, &out_path)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output difference image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
dECMC
CMC colour difference between two images.
viprs dECMC left.png right.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_dECMC(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output difference image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
deviate
Find the sample standard deviation of an image and print it.
viprs deviate in.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let value = raster.try_deviate(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
println!("{value}");
<IN>Input image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
divide
Divide the left image by the right (left / right).
viprs divide left.png right.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_divide(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
draw_circle
Draw a circle outline (or a filled disc with --fill) on an image.
viprs draw_circle in.png out.v cx.png cy.png radius.png --ink "r g b" --fill
let mut raster = io::load(&in_path, &limits)?;
if fill {
raster.try_draw_circle_filled(&ink, cx, cy, radius)?;
} else {
raster.try_draw_circle(&ink, cx, cy, radius)?;
}
io::save(&raster, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<CX>Centre x coordinate Required.
<CY>Centre y coordinate Required.
<RADIUS>Radius in pixels (>= 0) Required.
--ink<"r g b">Colour to paint, space-separated, one value per band of IN (e.g. "255 0 0") Required.
--fillFill the disc instead of drawing only the outline
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
draw_flood
Flood-fill a region of an image from a seed point.
viprs draw_flood in.png out.v x.png y.png --ink "r g b" --equal
let mut raster = io::load(&in_path, &limits)?;
// The flood wrappers are inherently fallible (ink width + off-canvas seed);
// `--equal` selects the "flood while equal to the seed value" blob variant.
if equal {
raster.draw_flood_blob(&ink, x, y)?;
} else {
raster.draw_flood(&ink, x, y)?;
}
io::save(&raster, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<X>Seed x coordinate (>= 0) Required.
<Y>Seed y coordinate (>= 0) Required.
--ink<"r g b">Colour to paint, space-separated, one value per band of IN (e.g. "255 0 0") Required.
--equalFlood the region of pixels EQUAL to the seed value (vips draw_flood_blob) instead of flooding until the ink-coloured boundary
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
draw_image
Paste a sub-image into an image at a given offset.
viprs draw_image in.png out.v sub.png x.png y.png --max-width PX --max-height PX
let mut raster = io::load(&in_path, &limits)?;
let sub = io::load(&sub_path, &limits)?;
raster.draw_image(&sub, x, y);
io::save(&raster, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<SUB>Sub-image to paste (must share IN's pixel format) Required.
<X>Target x of the sub-image's left edge Required.
<Y>Target y of the sub-image's top edge Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
draw_line
Draw a 1px line between two points on an image.
viprs draw_line in.png out.v x1.png y1.png x2.png y2.png --ink "r g b" --max-width PX
let mut raster = io::load(&in_path, &limits)?;
raster.try_draw_line(&ink, x1, y1, x2, y2)?;
io::save(&raster, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<X1>Start x coordinate Required.
<Y1>Start y coordinate Required.
<X2>End x coordinate Required.
<Y2>End y coordinate Required.
--ink<"r g b">Colour to paint, space-separated, one value per band of IN (e.g. "255 0 0") Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
draw_mask
Paint ink through a single-band 8-bit stencil onto an image.
viprs draw_mask in.png out.v mask.png x.png y.png --ink "r g b" --max-width PX
let mut raster = io::load(&in_path, &limits)?;
let mask = io::load(&mask_path, &limits)?;
// `try_draw_mask` rejects a float target with a typed error (the blend
// decodes unsigned 8/16-bit samples); the single-band 8-bit stencil is
// validated above (a non-conforming mask is a documented core no-op).
raster.try_draw_mask(&ink, &mask, x, y)?;
io::save(&raster, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<MASK>Single-band 8-bit stencil image (each pixel is the ink opacity 0..=255) Required.
<X>Target x of the mask's left edge Required.
<Y>Target y of the mask's top edge Required.
--ink<"r g b">Colour to paint, space-separated, one value per band of IN (e.g. "255 0 0") Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
draw_rect
Draw a rectangle outline (or a filled rectangle with --fill) on an image.
viprs draw_rect in.png out.v left.png top.png width.png height.png --ink "r g b" --fill
let mut raster = io::load(&in_path, &limits)?;
if fill {
raster.try_draw_rect_filled(&ink, left, top, width, height)?;
} else {
raster.try_draw_rect(&ink, left, top, width, height)?;
}
io::save(&raster, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<LEFT>Left edge x coordinate Required.
<TOP>Top edge y coordinate Required.
<WIDTH>Rectangle width in pixels Required.
<HEIGHT>Rectangle height in pixels Required.
--ink<"r g b">Colour to paint, space-separated, one value per band of IN (e.g. "255 0 0") Required.
--fillFill the rectangle instead of drawing only the outline
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
draw_smudge
Box-blur a rectangular region of an image in place.
viprs draw_smudge in.png out.v left.png top.png width.png height.png --max-width PX --max-height PX
let mut raster = io::load(&in_path, &limits)?;
raster.draw_smudge(left, top, width, height);
io::save(&raster, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<LEFT>Left edge x coordinate Required.
<TOP>Top edge y coordinate Required.
<WIDTH>Rectangle width in pixels Required.
<HEIGHT>Rectangle height in pixels Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
embed
Embed an image in a larger canvas at a given position.
viprs embed in.png out.v x.png y.png width.png height.png --extend black --background "r g b"
let raster = io::load(&in_path, &limits)?;
let out = raster.try_embed(x, y, width, height, extend, background.as_deref())?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<X>Left edge of the input in the output Required.
<Y>Top edge of the input in the output Required.
<WIDTH>Canvas width in pixels Required.
<HEIGHT>Canvas height in pixels Required.
--extend<black|copy|repeat|mirror|white|background>How to fill the pixels around the input One of:
black,copy,repeat,mirror,white,background. Default: black--background<"r g b">Background colour for --extend background (space-separated, one value or one per band)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
extract_area
Extract a rectangular region from an image.
viprs extract_area in.png out.v left.png top.png width.png height.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_extract_area(left, top, width, height)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<LEFT>Left edge of the extract area Required.
<TOP>Top edge of the extract area Required.
<WIDTH>Width of the extract area Required.
<HEIGHT>Height of the extract area Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
extract_band
Extract one band, or --n consecutive bands, from an image.
viprs extract_band in.png out.v band.png --n 1 --max-width PX
let raster = io::load(&in_path, &limits)?;
// A single `extract_band BAND` is `--n 1`; both go through the range API so
// the `--n` case is one code path (mirrors vips's single `extract_band` op).
let out = raster.try_extract_bands(band, n)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<BAND>First band to extract (>= 0) Required.
--n<N>Number of consecutive bands to extract Default: 1
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
eye
Make an image of the eye's spatial-frequency response.
viprs eye out.v width.png height.png --uchar
let raster = decode_file(&input)?;
let result = raster.try_eye()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
--ucharOutput an unsigned char image (vips --uchar)
falsecolour
Map an image through the libvips PET false-colour scale.
viprs falsecolour in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_falsecolour()?;
io::save(&out, &out_path)?;
<IN>Input image (band 0 is used) Required.
<OUT>Output sRGB image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
fastcor
Fast (sum-of-squared-differences) correlation of an image against a template.
viprs fastcor in.png ref.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let template = io::load(&ref_path, &limits)?;
let out = raster.try_fastcor(&template)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<REF>Template / reference image Required.
<OUT>Output correlation surface (.v) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
find_trim
Find the bounding box of non-background content and print left/top/width/height.
viprs find_trim in.png --background c… --max-width PX
let raster = decode_file(&input)?;
let value = raster.try_find_trim(background, max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
println!("{value}");
<IN>Input image Required.
--background<c…>Background colour as a space-separated vector (default 255 per band); vips's --threshold (default 10) and --line-art are not core-exposed
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
flatten
Flatten an alpha channel out against a background.
viprs flatten in.png out.v --background c… --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_flatten(background.as_deref())?;
io::save(&out, &out_path)?;
<IN>Input image with an alpha band Required.
<OUT>Output image Required.
--background<c…>Background value: a single constant for every band, or one space-separated value per output band (e.g. "255 0 0"). Default black.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
flip
Flip an image horizontally or vertically.
viprs flip in.png out.v direction.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = match dir {
"horizontal" => raster.try_fliphor()?,
"vertical" => raster.try_flipver()?,
other => bail!("unknown flip direction {other:?} (expected horizontal|vertical)"),
};
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<DIRECTION>Direction to flip the image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
fractsurf
Make a fractal surface.
viprs fractsurf out.v width.png height.png fractal_dimension.png
let raster = decode_file(&input)?;
let result = raster.try_fractsurf()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<FRACTAL_DIMENSION>Fractal dimension (vips 2..3) Required.
freqmult
Multiply an image by a mask in Fourier space.
viprs freqmult in.png mask.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let mask = io::load(&mask_path, &limits)?;
let out = raster.try_freqmult(&mask)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<MASK>Input mask image (a real float mask, same size as IN) Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
fwfft
Forward 2D fast Fourier transform (real or complex input → complex spectrum).
viprs fwfft in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_fwfft()?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output spectrum (write to .v — a complex float-pair raster) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
gamma
Apply a gamma curve to an image.
viprs gamma in.png out.v --exponent E --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_gamma(exponent)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
--exponent<E>Gamma factor (finite, in 1e-6..=1000; vips default 0.416667 = 1/2.4)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
gaussblur
Gaussian-blur an image.
viprs gaussblur in.png out.v sigma.png --min-ampl 0.2 --precision integer
let raster = io::load(&in_path, &limits)?;
let out = raster.try_gaussblur(sigma, min_ampl, precision)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<SIGMA>Sigma of the Gaussian (0..=1000) Required.
--min-ampl<A>Minimum amplitude of the Gaussian (0.001..=1) Default: 0.2
--precision<integer|float>Calculation precision (integer|float; vips's approximate is not core-backed) One of:
integer,float. Default: integer--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
gaussmat
Make a Gaussian mask matrix.
viprs gaussmat out.v sigma.png min-ampl.png --separable --precision integer
let kernel = Kernel::try_gaussmat(sigma, min_ampl, separable, precision)?;
save_matrix(&kernel, &out)?;
<OUT>Output matrix (.v) Required.
<SIGMA>Sigma of the Gaussian (1e-06..=10000) Required.
<MIN-AMPL>Minimum amplitude of the Gaussian (1e-06..=10000) Required.
--separableGenerate the separable (1xN centre row) form
--precision<integer|float>Calculation precision (integer|float; vips's approximate is not core-backed) One of:
integer,float. Default: integer
gaussnoise
Make a Gaussian-noise image (seeded PRNG).
viprs gaussnoise out.v width.png height.png --sigma 30 --mean 128
let raster = decode_file(&input)?;
let result = raster.try_gaussnoise(sigma, mean, seed)?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
--sigma<S>Standard deviation of the noise (vips --sigma, default 30, >= 0) Default: 30
--mean<M>Mean of the noise (vips --mean, default 128) Default: 128
--seed<N>PRNG seed (vips --seed gint, default 0; negatives honored) Default: 0
getpoint
Read one pixel and print its per-band values in vips numeric format.
viprs getpoint in.png x.png y.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
// Bounds-check before the call: core's getpoint asserts `x < width` /
// `y < height`, so an out-of-range coordinate must become exit 1, not abort.
if x >= raster.width() || y >= raster.height() {
bail!(
"getpoint: coordinate ({x}, {y}) is out of bounds for a {}x{} image",
raster.width(),
raster.height()
);
}
let pixel = raster.getpoint(x, y);
<IN>Input image Required.
<X>Column to read (>= 0) Required.
<Y>Row to read (>= 0) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
globalbalance
Balance the brightness of an assembled viprs mosaic.
viprs globalbalance in.png out.v --max-width PX --max-height PX
let mosaic = io::load(&in_path, &limits)?;
// Errors (no / corrupt join-tree metadata, unsolvable factor system) become
// exit 1; the output is always float, so callers write a `.v` sink.
let out = mosaic.try_global_balance()?;
io::save(&out, &out_path)?;
<IN>A mosaic built by `viprs merge`/`viprs mosaic` (carries join-tree metadata) Required.
<OUT>Output image (always float — write .v) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
gravity
Place an image within a larger canvas at a compass position.
viprs gravity in.png out.v direction.png width.png height.png --extend black --background "r g b"
let raster = io::load(&in_path, &limits)?;
let out = raster.try_gravity(direction, width, height, extend, background.as_deref())?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<DIRECTION>Compass position to place the image at Required.
<WIDTH>Canvas width in pixels Required.
<HEIGHT>Canvas height in pixels Required.
--extend<black|copy|repeat|mirror|white|background>How to fill the pixels around the input One of:
black,copy,repeat,mirror,white,background. Default: black--background<"r g b">Background colour for --extend background (space-separated, one value or one per band)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
grey
Create a horizontal grey ramp image.
viprs grey out.v width.png height.png --uchar
let out = Raster::try_grey(width, height, uchar)?;
io::save(&out, &out_path)?;
<OUT>Output image Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
--ucharOutput an 8-bit Gray8 ramp instead of the default float ramp
grid
Re-tile a tall stack of tiles into a grid.
viprs grid in.png out.v tile-height.png across.png down.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_grid(tile_height, across, down)?;
io::save(&out, &out_path)?;
<IN>Input image (a vertical tile stack) Required.
<OUT>Output image Required.
<TILE-HEIGHT>Height of each tile in pixels (>= 1) Required.
<ACROSS>Number of tiles across the output grid (>= 1) Required.
<DOWN>Number of tiles down the output grid (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_cum
Form the cumulative histogram (running sum).
viprs hist_cum in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_hist_cum()?;
io::save(&out, &out_path)?;
<IN>Input histogram Required.
<OUT>Output cumulative histogram Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_entropy
Print the Shannon entropy (bits) of a histogram.
viprs hist_entropy in.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let entropy = raster.try_hist_entropy()?;
<IN>Input histogram image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_equal
Histogram-equalise an image (each band independently).
viprs hist_equal in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.hist_equal();
io::save(&out, &out_path)?;
<IN>Input image (unsigned 8/16-bit) Required.
<OUT>Output equalised image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_find
Find the per-band value histogram of an image.
viprs hist_find in.png out.v --band -1 --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = match band {
None => raster.try_hist_find()?,
Some(b) => raster.try_hist_find_band(b)?,
};
io::save(&out, &out_path)?;
<IN>Input image (unsigned 8/16-bit) Required.
<OUT>Output histogram Required.
--band<N>Find the histogram of this band only (-1..=100000; -1 = all bands, the default) Default: -1
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_find_indexed
Sum image samples into bins selected by an index image. Bins combine by SUM only (vips's max/min combine modes are not supported by the core).
viprs hist_find_indexed in.png index.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let index = io::load(&index_path, &limits)?;
let out = raster.try_hist_find_indexed(&index)?;
io::save(&out, &out_path)?;
<IN>Input image (unsigned 8/16-bit) Required.
<INDEX>One-band index image selecting the output bin per pixel Required.
<OUT>Output histogram Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_find_ndim
Find an up-to-3-dimensional histogram of an image.
viprs hist_find_ndim in.png out.v --bins 10 --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_hist_find_ndim(Some(bins))?;
io::save(&out, &out_path)?;
<IN>Input image (unsigned 8/16-bit, 1-3 bands) Required.
<OUT>Output histogram cube Required.
--bins<N>Number of bins in each dimension (1..=65536; default 10) Default: 10
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_ismonotonic
Print whether a histogram is monotonically non-decreasing.
viprs hist_ismonotonic in.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let monotonic = raster.try_hist_ismonotonic()?;
<IN>Input histogram image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_local
Local (CLAHE) histogram equalisation over a sliding window.
viprs hist_local in.png out.v width.png height.png --max-slope 0 --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_hist_local(width, height, max_slope)?;
io::save(&out, &out_path)?;
<IN>Input image (unsigned 8/16-bit) Required.
<OUT>Output image Required.
<WIDTH>Window width in pixels (1..=100000000) Required.
<HEIGHT>Window height in pixels (1..=100000000) Required.
--max-slope<N>CLAHE contrast limit (0..=100; 0 = unlimited, the default) Default: 0
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_match
Build a LUT matching one histogram to a reference histogram.
viprs hist_match in.png ref.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let reference = io::load(&ref_path, &limits)?;
let out = raster.try_hist_match(&reference)?;
io::save(&out, &out_path)?;
<IN>Input histogram Required.
<REF>Reference histogram Required.
<OUT>Output matching LUT Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_norm
Normalise a histogram so each band's maximum equals the max index.
viprs hist_norm in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_hist_norm()?;
io::save(&out, &out_path)?;
<IN>Input histogram Required.
<OUT>Output normalised histogram Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hist_plot
Plot a one-band histogram as a bar-graph image.
viprs hist_plot in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_hist_plot()?;
io::save(&out, &out_path)?;
<IN>Input one-band histogram Required.
<OUT>Output plot image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hough_circle
Hough circle transform (scale 1), one band per radius in MIN..=MAX.
viprs hough_circle in.png out.v min_radius.png max_radius.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_hough_circle(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output accumulator image (.v) Required.
<MIN_RADIUS>Smallest radius to search for (>= 1) Required.
<MAX_RADIUS>Largest radius to search for (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
hough_line
Hough line transform into the fixed 256×256 accumulator.
viprs hough_line in.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_hough_line(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output accumulator image (.v) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
icc_export
Export a PCS image to a device colour space with an ICC profile.
viprs icc_export in.png out.v --output-profile PROFILE --intent perceptual
let raster = io::load(&in_path, &limits)?;
let out = raster.try_icc_export_with(depth, intent, output_profile.as_deref())?;
io::save(&out, &out_path)?;
<IN>Input PCS (Lab) image Required.
<OUT>Output device image Required.
--output-profile<PROFILE>ICC profile file to export to (default: the image's embedded icc-profile-data)
--intent<INTENT>Rendering intent (perceptual|relative|saturation|absolute; vips's `auto` is not core-backed) One of:
perceptual,relative,saturation,absolute. Default: relative--depth<BITS>Output device-space depth in bits (8 or 16) Default: 8
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
icc_import
Import a device image to the profile connection space with an ICC profile.
viprs icc_import in.png out.v --input-profile PROFILE --intent perceptual
let raster = io::load(&in_path, &limits)?;
let out = raster.try_icc_import_with(intent, input_profile.as_deref(), Some(pcs))?;
io::save(&out, &out_path)?;
<IN>Input device image Required.
<OUT>Output PCS image Required.
--input-profile<PROFILE>ICC profile file to import from (default: the image's embedded icc-profile-data)
--intent<INTENT>Rendering intent (perceptual|relative|saturation|absolute; vips's `auto` is not core-backed) One of:
perceptual,relative,saturation,absolute. Default: relative--pcs<PCS>Profile connection space of the output (lab|xyz) One of:
lab,xyz. Default: lab--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
icc_transform
Transform a device image to another device profile in one step.
viprs icc_transform in.png out.v output_profile.v --input-profile PROFILE --intent perceptual
let raster = io::load(&in_path, &limits)?;
// Compose import→export uniformly so `--intent`/`--depth` are honoured on
// both the `--input-profile` and the embedded-profile (input_profile=None)
// paths — the core `try_icc_transform` wrapper would silently substitute
// perceptual intent and the input's own depth.
let out = raster
.try_icc_import_with(intent, input_profile.as_deref(), None)?
.try_icc_export_with(depth, intent, Some(&output_profile))?;
io::save(&out, &out_path)?;
<IN>Input device image Required.
<OUT>Output device image Required.
<OUTPUT_PROFILE>ICC profile file to export to (positional, as in vips) Required.
--input-profile<PROFILE>ICC profile file to import from (default: the image's embedded icc-profile-data)
--intent<INTENT>Rendering intent (perceptual|relative|saturation|absolute; vips's `auto` is not core-backed) One of:
perceptual,relative,saturation,absolute. Default: relative--depth<BITS>Output device-space depth in bits (8 or 16) Default: 8
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
identity
Create a 1-D identity look-up table image.
viprs identity out.v --ushort
// Both constructors are infallible (fixed-size LUTs), so there is no
// `try_identity` to call.
let out = if ushort {
Raster::identity_ushort()
} else {
Raster::identity()
};
io::save(&out, &out_path)?;
<OUT>Output image Required.
--ushortCreate a 16-bit (65536×1 Gray16) LUT instead of the 8-bit (256×1) LUT
ifthenelse
Select pixels from two images under a condition image.
viprs ifthenelse cond.png in1.png in2.png out.v --max-width PX --max-height PX
let cond = io::load(&cond_path, &limits)?;
let then = io::load(&in1_path, &limits)?;
let otherwise = io::load(&in2_path, &limits)?;
let out = cond.try_ifthenelse(&then, &otherwise)?;
io::save(&out, &out_path)?;
<COND>Condition image (non-zero = pick IN1) Required.
<IN1>Source for TRUE (non-zero) pixels Required.
<IN2>Source for FALSE (zero) pixels Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
insert
Insert one image onto another at a position.
viprs insert main.png sub.png out.v x.png y.png --expand --background "r g b"
let main = io::load(&main_path, &limits)?;
let sub = io::load(&sub_path, &limits)?;
let out = main.try_insert(&sub, x, y, expand, background.as_deref())?;
io::save(&out, &out_path)?;
<MAIN>Main (background) image Required.
<SUB>Sub-image to insert Required.
<OUT>Output image Required.
<X>Left edge of the sub-image in the main image Required.
<Y>Top edge of the sub-image in the main image Required.
--expandExpand the output to hold all of both inputs (new pixels use --background, default black)
--background<"r g b">Background colour for --extend background (space-separated, one value or one per band)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
invertlut
Build an inverse look-up table from a matrix of measured (x, f(x)) rows.
viprs invertlut in.png out.v --size 256
let matrix = load_matrix(&in_path)?;
let out = matrix.try_invertlut_size(size)?;
io::save(&out, &out_path)?;
<IN>Input matrix of measured points (column 0 = input level, each further column = one band's response, all in 0..=1), as a vips text-matrix file Required.
<OUT>Output LUT path (use .v: a float matrix carrier) Required.
--size<N>LUT size to generate (vips range 1..=1000000, default 256) Default: 256
invfft
Inverse 2D fast Fourier transform; --real keeps only the real part.
viprs invfft in.png out.v --real --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = if real {
raster.try_invfft_real()?
} else {
raster.try_invfft()?
};
io::save(&out, &out_path)?;
<IN>Input image / spectrum Required.
<OUT>Output image (write to .v — complex, or real with --real) Required.
--realOutput only the real part of the transform (vips `invfft --real`, the folded `invfft_real` op)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
labelregions
Label 4-connected regions; write the label mask and print the segment count.
viprs labelregions in.png mask_out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let (mask, segments) = raster.try_label_regions()?;
io::save(&mask, &mask_out)?;
<IN>Input image Required.
<MASK_OUT>Output path for the Gray16 label mask Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
linear
Compute a·in + b (scalar, broadcast across bands); float out, or uchar with --uchar.
viprs linear in.png out.v a.png b.png --uchar --max-width PX
let raster = decode_file(&input)?;
let result = raster.try_linear(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<A>Multiplier as a space-separated vector (a single scalar is broadcast) Required.
<B>Addend as a space-separated vector (a single scalar is broadcast) Required.
--ucharClip and truncate the result into an 8-bit image (vips --uchar)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
logmat
Make a Laplacian-of-Gaussian mask matrix.
viprs logmat out.v sigma.png min-ampl.png --separable --precision integer
let kernel = Kernel::try_logmat(sigma, min_ampl, separable, precision)?;
save_matrix(&kernel, &out)?;
<OUT>Output matrix (.v) Required.
<SIGMA>Radius of the Gaussian (1e-06..=10000) Required.
<MIN-AMPL>Minimum amplitude of the Gaussian (1e-06..=10000) Required.
--separableGenerate the separable (1xN centre row) form
--precision<integer|float>Calculation precision (integer|float; vips's approximate is not core-backed) One of:
integer,float. Default: integer
mapim
Resample an image through a two-band coordinate index image.
viprs mapim in.png out.v index.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let index = io::load(&index_path, &limits)?;
let out = raster.try_mapim(&index, interpolate)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<INDEX>Two-band coordinate index image (band 0 = source x, band 1 = source y) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
--interpolate<I>Point interpolator (nearest|bilinear|bicubic|nohalo|lbb) One of:
nearest,bilinear,bicubic,nohalo,lbb. Default: bilinear
maplut
Map every sample of an image through a look-up table. The LUT applies to ALL bands (vips's per-band `--band` selection is not supported by the core).
viprs maplut in.png out.v lut.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let lut = io::load(&lut_path, &limits)?;
let out = raster.try_maplut(&lut)?;
io::save(&out, &out_path)?;
<IN>Input image (unsigned 8/16-bit) Required.
<OUT>Output mapped image Required.
<LUT>Look-up table image (histogram-shaped: Nx1 or 1xN) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
mask_butterworth
Make a Butterworth frequency-domain filter mask.
viprs mask_butterworth out.v width.png height.png order.png frequency_cutoff.png amplitude_cutoff.png --nodc --optical
let raster = decode_file(&input)?;
let result = raster.try_mask_butterworth()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<ORDER>Filter order (vips >= 1) Required.
<FREQUENCY_CUTOFF>Frequency cutoff (vips >= 0) Required.
<AMPLITUDE_CUTOFF>Amplitude cutoff (vips 0..1) Required.
--nodcRemove the DC component (vips --nodc)
--opticalRotate quadrants to optical space (vips --optical)
--ucharOutput an unsigned char image (vips --uchar)
mask_butterworth_band
Make a Butterworth band filter mask.
viprs mask_butterworth_band out.v width.png height.png order.png frequency_cutoff_x.png frequency_cutoff_y.png radius.png amplitude_cutoff.png --uchar --optical
let raster = decode_file(&input)?;
let result = raster.try_mask_butterworth_band()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<ORDER>Filter order (vips >= 1) Required.
<FREQUENCY_CUTOFF_X>Frequency cutoff x (vips >= 0) Required.
<FREQUENCY_CUTOFF_Y>Frequency cutoff y (vips >= 0) Required.
<RADIUS>Radius of the circle (vips >= 0) Required.
<AMPLITUDE_CUTOFF>Amplitude cutoff (vips 0..1) Required.
--ucharOutput an unsigned char image (vips --uchar)
--opticalRotate quadrants to optical space (vips --optical)
--nodcRemove the DC component (vips --nodc)
mask_butterworth_ring
Make a Butterworth ring filter mask.
viprs mask_butterworth_ring out.v width.png height.png order.png frequency_cutoff.png amplitude_cutoff.png ringwidth.png --nodc
let raster = decode_file(&input)?;
let result = raster.try_mask_butterworth_ring()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<ORDER>Filter order (vips >= 1) Required.
<FREQUENCY_CUTOFF>Frequency cutoff (vips >= 0) Required.
<AMPLITUDE_CUTOFF>Amplitude cutoff (vips 0..1) Required.
<RINGWIDTH>Ring width (vips >= 0) Required.
--nodcRemove the DC component (vips --nodc)
mask_fractal
Make a fractal power-spectrum filter mask.
viprs mask_fractal out.v width.png height.png fractal_dimension.png
let raster = decode_file(&input)?;
let result = raster.try_mask_fractal()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<FRACTAL_DIMENSION>Fractal dimension (vips 2..3) Required.
mask_gaussian
Make a Gaussian frequency-domain filter mask.
viprs mask_gaussian out.v width.png height.png frequency_cutoff.png amplitude_cutoff.png --nodc
let raster = decode_file(&input)?;
let result = raster.try_mask_gaussian()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<FREQUENCY_CUTOFF>Frequency cutoff (vips >= 0) Required.
<AMPLITUDE_CUTOFF>Amplitude cutoff (vips 0..1) Required.
--nodcRemove the DC component (vips --nodc)
mask_gaussian_band
Make a Gaussian band filter mask.
viprs mask_gaussian_band out.v width.png height.png frequency_cutoff_x.png frequency_cutoff_y.png radius.png amplitude_cutoff.png
let raster = decode_file(&input)?;
let result = raster.try_mask_gaussian_band()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<FREQUENCY_CUTOFF_X>Frequency cutoff x (vips >= 0) Required.
<FREQUENCY_CUTOFF_Y>Frequency cutoff y (vips >= 0) Required.
<RADIUS>Radius of the circle (vips >= 0) Required.
<AMPLITUDE_CUTOFF>Amplitude cutoff (vips 0..1) Required.
mask_gaussian_ring
Make a Gaussian ring filter mask.
viprs mask_gaussian_ring out.v width.png height.png frequency_cutoff.png amplitude_cutoff.png ringwidth.png --nodc
let raster = decode_file(&input)?;
let result = raster.try_mask_gaussian_ring()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<FREQUENCY_CUTOFF>Frequency cutoff (vips >= 0) Required.
<AMPLITUDE_CUTOFF>Amplitude cutoff (vips 0..1) Required.
<RINGWIDTH>Ring width (vips >= 0) Required.
--nodcRemove the DC component (vips --nodc)
mask_ideal
Make an ideal frequency-domain filter mask.
viprs mask_ideal out.v width.png height.png frequency_cutoff.png --nodc
let raster = decode_file(&input)?;
let result = raster.try_mask_ideal()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<FREQUENCY_CUTOFF>Frequency cutoff (vips >= 0) Required.
--nodcRemove the DC component (vips --nodc)
mask_ideal_band
Make an ideal band filter mask.
viprs mask_ideal_band out.v width.png height.png frequency_cutoff_x.png frequency_cutoff_y.png radius.png
let raster = decode_file(&input)?;
let result = raster.try_mask_ideal_band()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<FREQUENCY_CUTOFF_X>Frequency cutoff x (vips >= 0) Required.
<FREQUENCY_CUTOFF_Y>Frequency cutoff y (vips >= 0) Required.
<RADIUS>Radius of the circle (vips >= 0) Required.
mask_ideal_ring
Make an ideal ring filter mask.
viprs mask_ideal_ring out.v width.png height.png frequency_cutoff.png ringwidth.png --nodc
let raster = decode_file(&input)?;
let result = raster.try_mask_ideal_ring()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<FREQUENCY_CUTOFF>Frequency cutoff (vips >= 0) Required.
<RINGWIDTH>Ring width (vips >= 0) Required.
--nodcRemove the DC component (vips --nodc)
math
Apply a unary math (trig / log / exp) operation; float output.
viprs math in.png out.v op.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_math(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image (float; use .v) Required.
<OP>Math operation (trig args/results are in DEGREES; asin/acos need inputs in [-1,1], log/exp domains apply) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
math2
Apply a binary math (atan2 / pow / wop) operation; float output.
viprs math2 left.png right.png out.v op.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_math2(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output image (float; use .v) Required.
<OP>Binary math operation (pow = left^right, wop = right^left) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
math2_const
Binary math with a constant; only the power operation (pow) is core-backed.
viprs math2_const in.png out.v math2.png c.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_math2_const(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<MATH2>Math operation (only pow is core-backed) Required.
<C>Exponent constant (a single scalar; per-band vectors are not core-backed) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
matrixinvert
Invert a square matrix read from a vips text-matrix file.
viprs matrixinvert in.png out.v
let matrix = load_matrix(&in_path)?;
let out = matrix.try_matrixinvert()?;
io::save(&out, &out_path)?;
<IN>Input square matrix as a vips text-matrix file Required.
<OUT>Output matrix path (use .v: a float matrix carrier) Required.
max
Find the maximum sample of an image and print it (with --x/--y, its position).
viprs max in.png --x --y
let raster = decode_file(&input)?;
let value = raster.try_max(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
println!("{value}");
<IN>Input image Required.
--xAlso print the horizontal position of the first maximum
--yAlso print the vertical position of the first maximum
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
maxpair
Samplewise maximum of a pair of images.
viprs maxpair left.png right.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_maxpair(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
measure
Measure the mean of each patch in an H×V grid and write the matrix to a .v image.
viprs measure in.png out.v h.png v.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_measure(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output patch-means matrix (.v) Required.
<H>Number of patches across (>= 1) Required.
<V>Number of patches down (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
merge
Merge two overlapping images with a feathered seam.
viprs merge ref.png sec.png out.v direction.png dx.png dy.png --mblend 10 --max-width PX
let reference = io::load(&ref_path, &limits)?;
let secondary = io::load(&sec_path, &limits)?;
let out = reference.try_merge(&secondary, dir, dx, dy)?;
io::save(&out, &out_path)?;
<REF>Reference image (placed at the origin) Required.
<SEC>Secondary image (displaced by DX/DY) Required.
<OUT>Output image Required.
<DIRECTION>Join direction (REF on the left/top respectively) Required.
<DX>Horizontal displacement from SEC to REF Required.
<DY>Vertical displacement from SEC to REF Required.
--mblend<N>Maximum blend width (vips default 10; the core fixes this at 10, so any other value is rejected) Default: 10
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
min
Find the minimum sample of an image and print it (with --x/--y, its position).
viprs min in.png --x --y
let raster = decode_file(&input)?;
let value = raster.try_min(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
println!("{value}");
<IN>Input image Required.
--xAlso print the horizontal position of the first minimum
--yAlso print the vertical position of the first minimum
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
minpair
Samplewise minimum of a pair of images.
viprs minpair left.png right.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_minpair(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
morph
Erode or dilate a binary image with a morphological mask.
viprs morph in.png out.v mask.png op.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = match op {
"erode" => raster.try_erode(&mask_refs)?,
"dilate" => raster.try_dilate(&mask_refs)?,
other => bail!("unknown morph operation {other:?} (expected erode|dilate)"),
};
io::save(&out, &out_path)?;
<IN>Input image (8-bit) Required.
<OUT>Output image Required.
<MASK>Structuring element as a vips text matrix (values 0/128/255) Required.
<OP>Morphological operation Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
mosaic
Join two images by refining an approximate tie-point.
viprs mosaic ref.png sec.png out.v direction.png xref.png yref.png xsec.png ysec.png --max-width PX --max-height PX
let reference = io::load(&ref_path, &limits)?;
let secondary = io::load(&sec_path, &limits)?;
// The discrete tie-point search runs entirely inside try_mosaic (the search
// failure modes surface as typed errors → exit 1, never a panic).
let out = reference.try_mosaic(&secondary, dir, xref, yref, xsec, ysec)?;
io::save(&out, &out_path)?;
<REF>Reference image (placed at the origin) Required.
<SEC>Secondary image Required.
<OUT>Output image Required.
<DIRECTION>Join direction (REF on the left/top respectively) Required.
<XREF>Reference tie-point x (in REF) Required.
<YREF>Reference tie-point y (in REF) Required.
<XSEC>Secondary tie-point x (in SEC) Required.
<YSEC>Secondary tie-point y (in SEC) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
msb
Pick the most-significant byte of each sample.
viprs msb in.png out.v --band -1 --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_msb(band)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
--band<N>Band to take the MSB of (>= -1; -1 = all bands, the default) Default: -1
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
multiply
Multiply two images samplewise.
viprs multiply left.png right.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_multiply(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
percent
Print the threshold below which a given percent of pixels lie.
viprs percent in.png percent.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let threshold = raster.try_percent(percent)?;
<IN>Input image (unsigned 8/16-bit) Required.
<PERCENT>Percent of pixels (0..=100) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
perlin
Make a Perlin-noise image (seeded PRNG).
viprs perlin out.v width.png height.png --seed 0
let raster = decode_file(&input)?;
let result = raster.try_perlin(seed)?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
--seed<N>PRNG seed (vips --seed gint, default 0; negatives honored) Default: 0
phasecor
Phase correlation of two images (the peak is their translation).
viprs phasecor in.png in2.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let other = io::load(&in2_path, &limits)?;
let out = raster.try_phasecor(&other)?;
io::save(&out, &out_path)?;
<IN>First input image Required.
<IN2>Second input image (same size as IN) Required.
<OUT>Output correlation surface (write to .v — a real float raster) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
premultiply
Premultiply the colour bands by the alpha band.
viprs premultiply in.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_premultiply(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
profile
Find, per column and per row, the first non-zero sample position.
viprs profile in.png cols.png rows.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let value = raster.try_profile(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
println!("{value}");
<IN>Input image Required.
<COLS>Output columns image (.v) Required.
<ROWS>Output rows image (.v) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
project
Sum every column and every row; write the two projection images.
viprs project in.png cols.png rows.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let value = raster.try_project(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
println!("{value}");
<IN>Input image Required.
<COLS>Output column-sums image (.v) Required.
<ROWS>Output row-sums image (.v) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
rank
Rank (order-statistic) filter over a sliding window.
viprs rank in.png out.v width.png height.png index.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_rank(width, height, index)?;
io::save(&out, &out_path)?;
<IN>Input image (unsigned 8/16-bit) Required.
<OUT>Output image Required.
<WIDTH>Window width in pixels Required.
<HEIGHT>Window height in pixels Required.
<INDEX>Sorted-window index (0 = min, width*height-1 = max, middle = median) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
recomb
Recombine bands with a coefficient matrix (one output band per row).
viprs recomb in.png out.v m.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_recomb(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<M>Coefficient matrix as a vips text matrix file (one row per output band, one column per input band) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
reduce
Downsample an image with an anti-aliasing kernel.
viprs reduce in.png out.v hshrink.png vshrink.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_reduce(hshrink, vshrink, kernel)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<HSHRINK>Horizontal shrink factor (>= 1) Required.
<VSHRINK>Vertical shrink factor (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
--kernel<K>Resampling kernel (nearest|linear|cubic|mitchell|lanczos2|lanczos3) One of:
nearest,linear,cubic,mitchell,lanczos2,lanczos3. Default: lanczos3
reduceh
Downsample an image horizontally with a kernel.
viprs reduceh in.png out.v hshrink.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_reduceh(max_width, max_height, max_coord, max_pixels, max_alloc_bytes, kernel)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<HSHRINK>Horizontal shrink factor (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
--kernel<K>Resampling kernel (nearest|linear|cubic|mitchell|lanczos2|lanczos3) One of:
nearest,linear,cubic,mitchell,lanczos2,lanczos3. Default: lanczos3
reducev
Downsample an image vertically with a kernel.
viprs reducev in.png out.v vshrink.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_reducev(max_width, max_height, max_coord, max_pixels, max_alloc_bytes, kernel)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<VSHRINK>Vertical shrink factor (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
--kernel<K>Resampling kernel (nearest|linear|cubic|mitchell|lanczos2|lanczos3) One of:
nearest,linear,cubic,mitchell,lanczos2,lanczos3. Default: lanczos3
relational
Samplewise relational comparison of two images (0/255 mask out).
viprs relational left.png right.png out.v op.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_relational(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output image Required.
<OP>Relational operator to apply (left OP right) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
relational_const
Samplewise relational comparison against a constant (0/255 mask out).
viprs relational_const in.png out.v op.png c.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_relational_const(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<OP>Relational operator to apply (in OP c) Required.
<C>Constant compared against every sample. NOTE: vips takes a per-band constant vector; core applies ONE constant to all bands, so a single scalar is accepted here. Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
remainder_const
Remainder of every sample divided by a constant (format-preserving integer op).
viprs remainder_const in.png out.v c.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_remainder_const(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<C>Divisor constant (a single scalar; per-band vectors are not core-backed) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
replicate
Tile an image across and down.
viprs replicate in.png out.v across.png down.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_replicate(across, down)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<ACROSS>Repeat this many times horizontally Required.
<DOWN>Repeat this many times vertically Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
resize
Resize an image by a scale factor.
viprs resize in.png out.v scale.png --vscale S --max-width PX
let raster = io::load(&in_path, &limits)?;
let options = ResizeOptions {
vscale,
kernel,
..ResizeOptions::default()
};
let out = raster.try_resize_with(scale, options)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<SCALE>Scale factor (> 0) Required.
--vscale<S>Vertical scale factor (> 0); defaults to SCALE when omitted
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
--kernel<K>Resampling kernel (nearest|linear|cubic|mitchell|lanczos2|lanczos3) One of:
nearest,linear,cubic,mitchell,lanczos2,lanczos3. Default: lanczos3
rot
Rotate an image by a multiple of 90 degrees.
viprs rot in.png out.v angle.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_rot(angle)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<ANGLE>Angle to rotate clockwise Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
rot45
Rotate an odd-sided square image by a multiple of 45 degrees.
viprs rot45 in.png out.v --angle d0 --max-width PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_rot45(angle)?;
io::save(&out, &out_path)?;
<IN>Input image (odd-sided square) Required.
<OUT>Output image Required.
--angle<d0|d45|…|d315>Angle to rotate clockwise (vips default d45) One of:
d0,d45,d90,d135,d180,d225,d270,d315. Default: d45--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
rotate
Rotate an image by an arbitrary angle in degrees.
viprs rotate in.png out.v angle.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_rotate(max_width, max_height, max_coord, max_pixels, max_alloc_bytes, interpolate)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<ANGLE>Rotate clockwise by this many degrees Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
--interpolate<I>Point interpolator (nearest|bilinear|bicubic|nohalo|lbb) One of:
nearest,bilinear,bicubic,nohalo,lbb. Default: bilinear
round
Round every sample with the chosen rounding mode.
viprs round in.png out.v round.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_round(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<ROUND>Rounding operation Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
scale
Scale an image linearly (or with a log curve) to fill 0..=255 uchar.
viprs scale in.png out.v --log --max-width PX
let raster = decode_file(&input)?;
let result = raster.try_scale(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
--logUse the log-scaling curve instead of a linear stretch. (vips's --exp log-curve exponent is fixed at 0.25 in core and not configurable.)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
sdf
Make a signed distance field for a shape.
viprs sdf out.v width.png height.png shape.png --a 0 0 --b "x y"
let raster = decode_file(&input)?;
let result = raster.try_sdf(a, b, r, corners)?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
<SHAPE>Shape to render (vips VipsSdfShape) Required.
--a<"x y">Point a: circle centre / box corner / line start, INTEGER "x y" (vips --a; core SdfParams is integer-only — a §9 subset of vips's gdouble VipsArrayDouble, so "10.5 10.5" is rejected where vips would accept it) Default: 0 0
--b<"x y">Point b: opposite box corner / line end, INTEGER "x y" (vips --b; integer-only, §9 subset of vips's gdouble)
--r<R>Circle radius, INTEGER (vips --r, default 50, >= 0; integer-only, §9 subset of vips's gdouble — "16.5" is rejected where vips would accept it) Default: 50
--corners<"c0 c1 c2 c3">rounded-box corner radii, clockwise from top-right, INTEGER (vips --corners; integer-only, §9 subset of vips's gdouble)
sharpen
Unsharp-mask an image for print.
viprs sharpen in.png out.v --sigma 0.5 --m1 0
let raster = io::load(&in_path, &limits)?;
let out = raster.try_sharpen(sigma, m1, m2)?;
io::save(&out, &out_path)?;
<IN>Input image (sRGB / mono) Required.
<OUT>Output image Required.
Sigma of the blur Gaussian (1e-06..=10) Default: 0.5
Slope for flat areas (vips default 0) Default: 0
Slope for jaggy areas (vips default 3) Default: 3
Reject inputs wider than PX pixels (DecodeLimits::max_width)
Reject inputs taller than PX pixels (DecodeLimits::max_height)
Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
shrink
Shrink an image by integer or fractional factors with a box filter.
viprs shrink in.png out.v hshrink.png vshrink.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_shrink(hshrink, vshrink)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<HSHRINK>Horizontal shrink factor (>= 1) Required.
<VSHRINK>Vertical shrink factor (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
shrinkh
Shrink an image horizontally by an integer factor.
viprs shrinkh in.png out.v hshrink.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_shrinkh(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<HSHRINK>Horizontal shrink factor (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
shrinkv
Shrink an image vertically by an integer factor.
viprs shrinkv in.png out.v vshrink.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_shrinkv(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<VSHRINK>Vertical shrink factor (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
sign
Unit sign of every sample (−1 / 0 / 1; unsigned input yields 0/1).
viprs sign in.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_sign(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
similarity
Rotate and scale an image (similarity transform).
viprs similarity in.png out.v --scale 1 --angle 0
let raster = decode_file(&input)?;
let result = raster.try_similarity(scale, angle, max_width, max_height, max_coord, max_pixels, max_alloc_bytes, interpolate)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
--scale<S>Scale by this factor (> 0) Default: 1
--angle<DEG>Rotate clockwise by this many degrees Default: 0
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
--interpolate<I>Point interpolator (nearest|bilinear|bicubic|nohalo|lbb) One of:
nearest,bilinear,bicubic,nohalo,lbb. Default: bilinear
sines
Make a 2D sine grating.
viprs sines out.v width.png height.png
let raster = decode_file(&input)?;
let result = raster.try_sines()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
smartcrop
Crop to the most interesting area of an image.
viprs smartcrop in.png out.v width.png height.png --interesting centre --premultiplied
let raster = io::load(&in_path, &limits)?;
let (out, ax, ay) = raster.try_smartcrop(width, height, interesting, premultiplied)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<WIDTH>Width of the crop area Required.
<HEIGHT>Height of the crop area Required.
--interesting<centre|entropy|attention|low|high|all>How to measure interestingness (vips's `none` is excluded — not core-backed); prints the attention centre `x y` to stdout One of:
centre,entropy,attention,low,high,all. Default: attention--premultipliedThe input's alpha is already premultiplied (skip the internal premultiply)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
spcor
Normalised spatial cross-correlation of an image against a template.
viprs spcor in.png ref.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let template = io::load(&ref_path, &limits)?;
let out = raster.try_spcor(&template)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<REF>Template / reference image Required.
<OUT>Output correlation surface (.v) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
spectrum
Make a displayable log-scaled power spectrum.
viprs spectrum in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_spectrum()?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output spectrum (a uchar image; DC is moved to the centre) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
stats
Compute a per-band statistics matrix and write it to a .v image.
viprs stats in.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_stats(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output statistics matrix (.v; 6 columns min/max/sum/sum2/mean/sd) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
stdif
Statistical differencing over a sliding window.
viprs stdif in.png out.v width.png height.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_stdif(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<WIDTH>Window width in pixels (1..=256) Required.
<HEIGHT>Window height in pixels (1..=256). (vips's --a --m0 --b --s0 tuning knobs are fixed at core defaults 0.5/128/0.5/50.) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
subsample
Subsample (point-decimate) an image by integer factors.
viprs subsample in.png out.v xfac.png yfac.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_subsample(xfac, yfac)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<XFAC>Horizontal subsample factor (top-left of each cell; vips's --point averaging mode is not core-backed) Required.
<YFAC>Vertical subsample factor Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
subtract
Subtract the right image from the left (left - right).
viprs subtract left.png right.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_subtract(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<LEFT>Left-hand input image Required.
<RIGHT>Right-hand input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
sum
Sum an array of two or more images into one image.
viprs sum inputs.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let value = raster.try_sum(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
println!("{value}");
<INPUTS>Two or more input images followed by the output path Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
switch
Find the index of the first non-zero condition image at each pixel.
viprs switch inputs.png --max-width PX --max-height PX
let rasters: Vec<Raster> = inputs
.iter()
.map(|p| io::load(p, &limits))
.collect::<Result<_>>()?;
let refs: Vec<&Raster> = rasters.iter().collect();
let out = Raster::try_switch(&refs)?;
io::save(&out, &out_path)?;
<INPUTS>Two or more single-band condition images followed by the output path Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
text
Render text to a coverage image.
viprs text out.v text.png --dpi DPI --width PX
let raster = decode_file(&input)?;
let result = raster.try_text(dpi, width, height, wrap)?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<TEXT>Text to render Required.
--dpi<DPI>Render resolution (vips --dpi, default 72)
--width<PX>Maximum image width in pixels; wraps lines (vips --width)
--height<PX>Maximum image height in pixels; enables dpi auto-fit (vips --height)
--wrap<word|char|word-char|none>Line-wrap mode (vips --wrap, default word) One of:
word,char,word-char,none.
thumbnail
Make a thumbnail from an image file, fitting a box.
viprs thumbnail filename.png out.v width.png --height PX --crop INTERESTING
let raster = decode_file(&input)?;
let result = raster.try_thumbnail(height, crop, export_profile, max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<FILENAME>Source image file to read from Required.
<OUT>Output image Required.
<WIDTH>Fit within this width (>= 1) Required.
--height<PX>Fit within this height (>= 1); a square WIDTH box when omitted
--crop<INTERESTING>Fill the box and centre-crop (vips --crop; core supports none|centre only — `--crop` == `--crop centre`)
--linearReduce in linear light (square WIDTH box; not combinable with --height/--crop)
--export-profile<NAME>Export through the embedded ICC profile to this output profile (only "srgb"; square WIDTH box; not combinable with the others)
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
thumbnail_image
Make a thumbnail from an already-loaded image.
viprs thumbnail_image in.png out.v width.png --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_thumbnail_image(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
<WIDTH>Fit within a square WIDTH box (>= 1) Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
tonelut
Build a print tone-mapping look-up table.
viprs tonelut out.v
let raster = decode_file(&input)?;
let result = raster.try_tonelut()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
unpremultiply
Unpremultiply (divide) the colour bands by the alpha band.
viprs unpremultiply in.png out.v --max-width PX --max-height PX
let raster = decode_file(&input)?;
let result = raster.try_unpremultiply(max_width, max_height, max_coord, max_pixels, max_alloc_bytes)?;
save_file(&result, &output)?;
<IN>Input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
worley
Make a Worley-noise image (seeded PRNG).
viprs worley out.v width.png height.png --seed 0
let raster = decode_file(&input)?;
let result = raster.try_worley(seed)?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
--seed<N>PRNG seed (vips --seed gint, default 0; negatives honored) Default: 0
wrap
Wrap the image origin to its centre, swapping quadrants.
viprs wrap in.png out.v --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_wrap()?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
xyz
Make an image whose pixels are their own coordinates.
viprs xyz out.v width.png height.png
let raster = decode_file(&input)?;
let result = raster.try_xyz()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
zone
Make a zone-plate test pattern.
viprs zone out.v width.png height.png
let raster = decode_file(&input)?;
let result = raster.try_zone()?;
save_file(&result, &output)?;
<OUT>Output image path Required.
<WIDTH>Image width in pixels (>= 1) Required.
<HEIGHT>Image height in pixels (>= 1) Required.
zoom
Zoom in by integer pixel replication.
viprs zoom in.png out.v xfac.png yfac.png --max-width PX --max-height PX
let raster = io::load(&in_path, &limits)?;
let out = raster.try_zoom(xfac, yfac)?;
io::save(&out, &out_path)?;
<IN>Input image Required.
<OUT>Output image Required.
<XFAC>Horizontal zoom factor Required.
<YFAC>Vertical zoom factor Required.
--max-width<PX>Reject inputs wider than PX pixels (DecodeLimits::max_width)
--max-height<PX>Reject inputs taller than PX pixels (DecodeLimits::max_height)
--max-coord<PX>Reject a single axis larger than PX pixels (DecodeLimits::max_coord)
--max-pixels<N>Reject inputs with more than N total pixels (DecodeLimits::max_pixels)
--max-alloc-bytes<BYTES>Reject a decode allocation larger than BYTES (DecodeLimits::max_alloc_bytes)
Examples
Basic pyramid generation
DeepZoom from an image (default settings)
$ viprs pyramid photo.jpg tiles/
Produces a DeepZoom pyramid at tiles/ with a companion tiles.dzi manifest. Tiles are 256×256 PNG with no overlap. The monolithic engine holds the entire image in memory.
XYZ layout with JPEG tiles
$ viprs pyramid aerial.tiff map_tiles/ \
--layout xyz --format jpeg --quality 90
Outputs {z}/{x}/{y}.jpeg tiles ready to serve to MapLibre GL, Leaflet, or any XYZ tile client. JPEG reduces tile size ~5× vs PNG for photographic content.
Google Maps layout with centring
$ viprs pyramid gigapixel.png google_tiles/ \
--layout google --centre
Google layout uses power-of-2 tile grids. --centre places the image in the middle of the canvas with even padding.
DeepZoom with overlap for OpenSeadragon
$ viprs pyramid scan.png viewer_tiles/ \
--tile-size 512 --overlap 1
512px tiles with 1px overlap. OpenSeadragon uses the overlap to blend adjacent tiles for seamless rendering at high zoom.
PDF inputs
Scanned document (raster extraction)
$ viprs pyramid scan.pdf tiles/ --page 2
Extracts the embedded raster image from page 2 of the PDF. Fast and lossless — no rendering needed for scanned documents.
Vector PDF rendering with PDFium
$ viprs pyramid document.pdf tiles/ \
--render --dpi 300
Renders the vector PDF at 300 DPI using PDFium. Required for vector-authored PDF exports, text-heavy documents, and PDFs with paths/shapes instead of embedded images.
Match page dimensions after extraction
$ viprs pyramid scan.pdf tiles/ \
--match-page-size --dpi 150
Extracts the embedded raster, then resizes it to match the PDF page dimensions at 150 DPI. Produces output consistent with libvips' default PDF handling.
Blank tile optimization
Skip blank tiles on sparse images
$ viprs pyramid whiteboard.png tiles/ --skip-blank
Done: 341 tiles, 11 levels, peak memory 12.5 MB, 2.31s (287 blank tiles skipped)
Replaces 287 uniform-color tiles with 1-byte placeholders. Total output drops from ~45 MB to ~8 MB on a typical whiteboard scan.
Multi-threaded execution
Parallel tile extraction (monolithic engine)
$ viprs pyramid large.png tiles/ \
--concurrency 8 --buffer-size 128
8 worker threads extract tiles in parallel from the full-resolution canvas. Buffer of 128 tiles smooths out disk I/O variance. Best when the image fits comfortably in RAM.
Memory-constrained processing
Streaming engine with explicit budget
$ viprs pyramid huge_scan.png tiles/ \
--memory-budget 128
Streaming: budget 128.0 MB, strip_height=2048, estimated peak 98.4 MB
Done: 4521 tiles, 15 levels, peak memory 97.2 MB, 14.8s
Processes the image in 2048px-tall horizontal strips, keeping peak memory under 128 MB. The monolithic engine would need ~2 GB for this image.
Auto-select streaming budget
$ viprs pyramid huge_scan.png tiles/ --memory-budget 0
Budget is set to 1/4 of the estimated monolithic peak. The engine auto-selects: monolithic if the image is small, streaming otherwise. Best default for unknown image sizes.
Hard memory limit (safety net)
$ viprs pyramid huge.pdf tiles/ \
--memory-limit 512 --memory-budget 128 --render --dpi 300
Exits with an error if the estimated monolithic peak exceeds 512 MB, then uses streaming with a 128 MB budget. Double protection for containers with hard memory limits.
Parallel MapReduce engine
MapReduce with concurrent strip rendering
$ viprs pyramid huge_scan.png tiles/ \
--memory-budget 256 --parallel
MapReduce: budget 256.0 MB, strip_height=2048, 4 in-flight strips, estimated peak 198.7 MB
Done: 4521 tiles, 15 levels, peak memory 195.3 MB, 9.2s
Renders 4 strips concurrently within the 256 MB budget. ~35% faster than sequential streaming on a 4-core machine. Output is byte-identical.
MapReduce with per-strip tile workers
$ viprs pyramid huge_scan.png tiles/ \
--memory-budget 256 --parallel --concurrency 4
Each strip also uses 4 tile-extraction workers for an additional level of parallelism. Best on systems with 8+ cores and fast storage.
MapReduce with auto budget and blank skipping
$ viprs pyramid document.pdf tiles/ \
--memory-budget 0 --parallel --skip-blank \
--layout google --centre
Full-featured pipeline: auto budget, parallel processing, blank tile optimization, Google layout with centring. The engine chooses the optimal strip height and in-flight count.
Geo-referenced tiles
Geo-referenced DeepZoom pyramid
$ viprs pyramid aerial.png tiles/ \
--geo-origin "-73.9857,40.7484" --geo-scale "0.00001,-0.00001"
Maps the top-left pixel to (73.9857°W, 40.7484°N) with ~1.1m per pixel. Enables map viewers to overlay the pyramid on a world map.
Inspection and planning
Inspect a PDF file
$ viprs info document.pdf
PDF: document.pdf
Pages: 3
Page 1: 841.89 x 595.28 pt (A4 landscape)
Image: 3508 x 2480 px (JPEG, 1234567 bytes)
Page 2: 841.89 x 595.28 pt
Image: 3508 x 2480 px (JPEG, 987654 bytes)
Page 3: 841.89 x 595.28 pt
(no embedded raster — use --render)
Shows page dimensions, embedded raster details, and whether PDFium rendering is needed.
Preview the pyramid plan
$ viprs plan document.pdf --tile-size 256 --layout google --centre
Shows levels, tile counts, and canvas dimensions without generating any tiles. Use this to estimate output size and pick the right tile parameters.
Plan from explicit dimensions
$ viprs plan 16820 --height 11888 --layout google --centre
Plan using raw pixel dimensions instead of reading from a file.
Test images
Generate a test image for benchmarking
$ viprs test-image gradient.png --width 4096 --height 4096
Creates a 4096×4096 RGB8 gradient image. Useful for benchmarking the tile engine without needing real production images.
Full pipeline test (generate + tile)
$ viprs test-image /tmp/test.png --width 2048 --height 2048
$ viprs pyramid /tmp/test.png /tmp/tiles/ \
--memory-budget 8 --parallel --concurrency 4 --skip-blank
End-to-end smoke test: create a synthetic image, then tile it with every engine feature enabled.
Stdin input
Pipe an image from another command
$ curl -s https://example.com/scan.png | viprs pyramid - tiles/
Use - as the input to read from stdin. Works with any command that outputs image data.
Object-storage sink
Write pyramid directly to S3
$ viprs pyramid document.pdf --sink s3://my-bucket/tiles/set-a/ \
--format png --manifest-emit-checksums
Tiles are streamed to S3 without a local temp directory. Credentials are read from the standard AWS credential chain (AWS_ACCESS_KEY_ID, instance role, etc.). Requires compile-time --features s3.
Packfile archive
Archive a pyramid into a single .tar.gz
$ viprs pyramid scan.tiff --sink packfile://scan_tiles.tar.gz \
--layout deep-zoom --format png
Outputs a single scan_tiles.tar.gz containing the full tile tree and the .dzi manifest. Requires compile-time --features packfile.
Resumable job
Resume an interrupted run
# First run (interrupted)
$ viprs pyramid huge.pdf tiles/ --manifest-emit-checksums
^C # killed after 3200/4521 tiles
# Resume from checkpoint
$ viprs pyramid huge.pdf tiles/ --resume --manifest-emit-checksums
Resuming: 3200 tiles already written, 1321 remaining
Done: 4521 tiles, 15 levels, 1321 new tiles written, 6.3s
The checkpoint file tiles/.libviprs-job.json records each completed tile. On resume, only missing tiles are re-rendered.
Verify a completed run
$ viprs pyramid huge.pdf tiles/ --verify
Verified 4521 tiles. All checksums match.
Reads every tile and confirms it matches the stored digest. Non-zero exit if any tile is missing or corrupt.
Retry and failure policy
Retry transient errors, skip permanently failing tiles
$ viprs pyramid document.pdf --sink s3://my-bucket/out/ \
--retry-max 5 --retry-backoff 500 \
--failure-policy retry-then-skip
Each failing tile is retried up to 5 times with 500 ms initial back-off. If still failing, the tile is recorded as missing in the manifest and the job continues. Useful for long-running S3 uploads over unreliable links.
Blank-tile tolerance
Collapse near-white tiles on a noisy scan
$ viprs pyramid noisy_scan.pdf tiles/ --skip-blank --blank-tolerance 2
Done: 341 tiles, 11 levels, peak memory 12.1 MB, 2.18s (312 blank tiles skipped)
Tiles where every pixel channel is within ±2 of the background value are treated as blank. Without --blank-tolerance, scanner noise would prevent most margin tiles from matching the exact background colour.
Checksums and deduplication
Emit Blake3 checksums and deduplicate blank tiles
$ viprs pyramid document.pdf tiles/ \
--manifest-emit-checksums --checksum-algo blake3 \
--dedupe-blanks
The manifest records a Blake3 digest for each tile. A single copy of the blank placeholder is stored; all other blank tile paths are symlinks to it.
Full deduplication with content hashing
$ viprs pyramid document.pdf tiles/ --dedupe-all
Every tile is hashed after encoding. Duplicate content (common in tiled vector artwork) is stored once. Inode count can drop by 40–60% on typical document pyramids.
Tracing
Emit info-level spans
$ viprs pyramid document.pdf tiles/ --trace-level info
Emits libviprs::pipeline, libviprs::level, and libviprs::tile spans. Wire up a tracing-opentelemetry subscriber before calling viprs from a Rust driver, or export via the OTEL_EXPORTER_OTLP_ENDPOINT environment variable. Requires compile-time --features tracing.
Engine Comparison
| Scenario | Flags | Engine | Best for |
|---|---|---|---|
| Small image, fast disk | (none) | Monolithic | Images that fit in RAM |
| Small image, multi-core | --concurrency 8 |
Monolithic (parallel tiles) | Throughput on multi-core |
| Large image, limited RAM | --memory-budget 128 |
Streaming | Memory-constrained containers |
| Large image, multi-core | --memory-budget 256 --parallel |
MapReduce | Fast + memory-bounded |
| Large image, max throughput | --memory-budget 256 --parallel --concurrency 8 |
MapReduce (full parallel) | Dedicated build servers |
All engines produce byte-identical output. Choose based on available memory and CPU cores.
Cargo Features
Optional capabilities are gated behind Cargo features. Pass them at build time:
$ cargo install --path . --features s3,tracing,checksum,dedupe
| Feature | Default | Enables |
|---|---|---|
pdfium | no | --render flag (dynamic libpdfium) |
pdfium-static | no | --render flag (static libpdfium, larger binary) |
s3 | no | --sink s3://… |
tracing | no | --trace-level |
packfile | no | --sink packfile://… |
checksum | no | --manifest-emit-checksums, --checksum-algo, --verify |
dedupe | no | --dedupe-blanks, --dedupe-all |
Exit Status
| Code | Meaning |
|---|---|
0 | Success |
1 | Error (invalid args, file not found, memory limit exceeded, engine failure) |
See Also
libviprs home — API documentation — GitHub repository — libvips (the project that inspired libviprs)