12 KiB
pm-image: Fast image resizing for CLI, servers, and Windows Explorer
pm-image is a native C++ image toolkit built on libvips — the same high-performance engine behind tools like Sharp (Node.js). One binary gives you a command-line interface, an HTTP REST server, and line-delimited JSON IPC for automation. On Windows, optional Explorer integration adds right-click resize and convert workflows without opening another app.
Whether you batch photos for the web, power a local resize API, or shrink folders of RAW and HEIC files from the desktop, pm-image keeps pipelines predictable: decode → process → encode, with Sharp-like options (fit modes, quality, metadata stripping, smart crop) and optional disk caching so repeat jobs stay instant.
Download - Windows - AMD 64 | Source Code
Who is this for?
- Developers who want a single native binary (no Node runtime) for resize jobs in scripts, CI, or sidecar services.
- Windows power users who want context-menu resize presets and JPG conversion from File Explorer.
- Integrators who need REST (
POST /v1/resizeas JSON with server paths, or multipart upload with the resized image returned in the body) or IPC (one JSON line per request) with the same JSON schema as the CLI for path-based jobs.
How to resize images (quick start)
1. Command line — one file
Install or build pm-image, then run resize with input and output paths. By default, the image fits inside your max width and height without enlarging (unless you allow enlargement).
pm-image resize photo.jpg out.jpg --max-width 1920 --max-height 1080
- Quality (JPEG, WebP, AVIF): use
-q/quality(for example-q 85). - Output format: use the destination extension (
.webp,.avif,.jpg) or--formatwhen the path is ambiguous.
2. Fit modes — not every resize is “shrink to fit”
| Goal | What to use |
|---|---|
| Fit inside a box (keep aspect ratio, no crop) | Default behavior with --max-width / --max-height; optional --fit inside style semantics via fit in JSON. |
| Square avatar or thumbnail (crop) | Same --max-width and --max-height (e.g. 512), --fit cover, optional --position attention or entropy for smarter crops. |
| Square with padding (no crop) | --fit contain plus --background '#rrggbb' for letterboxing. |
| Exact dimensions by stretching | --fit fill (distorts if aspect ratios differ). |
Examples (see the package README for full flag lists):
# Thumbnail: 400×400 cover crop
pm-image resize in.jpg thumb.jpg --fit cover --max-width 400 --max-height 400
# Padded square on a dark canvas
pm-image resize wide.jpg square.jpg --fit contain --max-width 800 --max-height 800 --background '#111111'
3. Batch paths and folders
- Use globs (
*,?,**) in the input to process many files in one invocation. - Point output to a directory (often with a trailing
/or\) or use templates like${SRC_DIR},${SRC_NAME},${SRC_FILE_EXT}to mirror or rename outputs per file.
pm-image resize './photos/**/*.jpg' ./out/
pm-image resize --src './shots/*.jpg' --dst '${SRC_DIR}/${SRC_NAME}_medium.jpg' --max-width 800
4. HTTP API
Start the server (serve). POST /v1/resize supports two modes:
- JSON (
Content-Type: application/json):inputandoutputpaths on the machine running the server, plus the same resize fields as the CLI (max_width,max_height,fit,quality, glob batch, cache, …). Response is JSON (ok, and optionallycount/outputsfor batches). - Multipart upload (
Content-Type: multipart/form-data): send the image in a part namedfile,image, orupload. Add optional fields (max_width,format, …) as additional form parts. The response body is the encoded image (not JSON), withContent-Typeset from the output format (default JPEG ifformatis omitted). Uploads do not use the on-disk output cache.
See the C++ README (serve — HTTP REST) for curl examples.
5. Windows — resize from Explorer
After pm-image register-explorer (or the NSIS installer), use the PM-Media menu on images or folders for preset widths, in place or copy, plus convert to JPG. Override preset widths with --widths when registering.
6. Optional: Windows GUI
On Windows, pm-image resize --ui opens a native dialog to pick files and options (no --src / --dst with --ui).
Features at a glance
- libvips-backed resizing, rotation, flip/flop, EXIF autorotate, metadata strip (defaults match common web workflows).
- Formats: JPEG, PNG, WebP, TIFF; AVIF / HEIC when your libvips build includes HEIF; other formats depend on libvips loaders/savers.
- HTTP(S) URLs as
input(fetched via libcurl) for download-and-resize pipelines. - REST multipart upload: resize without writing
input/outputpaths — return the image directly to the client. - Output cache: SHA-256 keyed by path, size, mtime, and options — cache hits skip re-encoding (JSON/CLI/IPC path-based jobs; not used for multipart upload responses).
For prerequisites, build steps, installer, and the full option tables, see the C++ README.
FAQ (questions and answers)
What is pm-image?
pm-image is a CMake-built executable that exposes image resizing and related operations through CLI, serve (REST), and ipc (JSON lines). Processing uses libvips, aligned with a Sharp-like option model for familiarity.
Is pm-image the same as Sharp?
No — Sharp is a Node.js library; pm-image is a standalone native program. Both lean on libvips, so concepts like fit, position, kernel, and quality map closely. See the README’s “Sharp-like options” table for field names (without_enlargement, strip_metadata, etc.).
How do I resize an image to an exact width?
Use --max-width (and optionally --max-height). The image scales down (or up if you allow enlargement) to respect those bounds and the chosen fit mode. For a fixed width-only workflow, set a large max_height or use JSON/resize options that match your desired behavior.
How do I resize without losing quality?
Resizing always re-encodes for raster formats; “lossless” depends on format:
- For JPEG, use a high
-q(e.g. 90–95) and avoid repeated saves. - For PNG, resizing may still recompress; use PNG compression settings as needed.
- Prefer WebP or AVIF with tuned quality for smaller files at a given visual fidelity.
without enlargement defaults to true, so small images are not upscaled unless you opt in.
How do I batch resize images in a folder?
Use a glob for input and a directory or template for output. Multiple matches are processed in one command; see Batch paths & cache in the README for rules on trailing slashes and ${SRC_*} variables.
Can I resize images from URLs?
Yes. Pass an http:// or https:// URL as input. Redirects are followed; timeouts and redirect limits are configurable (--url-timeout, --url-max-redirects).
Does pm-image work on Windows?
Yes. Use the vips dev bundle (npm run setup:vips from the package), build pm-image, and optionally install NSIS output for pm-image-setup.exe, PATH, DLLs, and Explorer scripts. register-explorer adds context menus for resize and convert.
How does caching work?
When enabled, outputs are stored under a cache directory (default under the process working directory), keyed by a SHA-256 hash of input identity, file metadata, and all resize options. Identical jobs reuse cached bytes without running libvips again.
What is IPC for?
ipc accepts one JSON object per line over TCP (or a Unix socket on non-Windows), ideal for local daemons or tools that want a simple request/response channel without HTTP overhead.
Where is the full API reference?
- CLI:
pm-image --help,pm-image resize --help. - Library-level image API (underlying engine): libvips
VipsImage.
Related documentation
CLI examples
Paths below use Unix style; on Windows run pm-image.exe and use .\ or full paths as needed.
Help and version
pm-image --help
pm-image resize --help
pm-image -v
resize — fit inside a box (default), write WebP / AVIF by extension
# Max 800×600, stay inside the box, Lanczos3 (default), write JPEG quality 85 (default)
pm-image resize photo.jpg out.jpg --max-width 800 --max-height 600
# Same, explicit quality
pm-image resize photo.jpg out.jpg --max-width 800 --max-height 600 -q 90
# WebP output (quality applies)
pm-image resize photo.jpg thumb.webp --max-width 400 --max-height 400 -q 82
# AVIF output (quality applies; needs HEIF/AVIF support in your libvips build)
pm-image resize photo.png out.avif --max-width 1200 --max-height 1200 -q 50
# Force output format when the path has no extension you trust
pm-image resize in.tif /tmp/out --format webp --max-width 512
resize — square images (1:1)
Use the same --max-width and --max-height (that value is the square side in pixels). Pick --fit:
fit |
Result |
|---|---|
cover |
Fills the square; crops overflow (default crop: --position centre, or attention / entropy for smart crop). |
contain |
Full image inside the square; letterboxing on two sides if needed (--background). |
fill |
Stretches to the square (ignores aspect ratio). |
# 512×512 crop-to-square (avatars, thumbnails)
pm-image resize portrait.jpg avatar.jpg --fit cover --max-width 512 --max-height 512
# 1080×1080 WebP, smart crop on subject
pm-image resize product.png grid.webp --fit cover --max-width 1080 --max-height 1080 --position attention -q 85
# Square canvas, no crop — padded bands with a colour
pm-image resize panoramic.jpg square.jpg --fit contain --max-width 800 --max-height 800 --background '#111111'
# Exact square by stretching (rare)
pm-image resize any.jpg out.jpg --fit fill --max-width 256 --max-height 256
REST / IPC JSON: e.g. "max_width": 512, "max_height": 512, "fit": "cover", "position": "attention".
resize — cover (crop), contain (letterbox), rotate / flip
# Cover: fill 1200×630, crop centre (use --position attention for smart crop)
pm-image resize wide.jpg social.jpg --fit cover --max-width 1200 --max-height 630
# Contain: fit inside 800×600 canvas, letterbox with a background
pm-image resize logo.png padded.png --fit contain --max-width 800 --max-height 600 --background '#1a1a1a'
# EXIF autorotate (default), then rotate 90° CCW, vertical flip
pm-image resize img.jpg rotated.jpg --max-width 1024 --rotate 90 --flip
serve — HTTP REST
# Default: http://127.0.0.1:8080 — GET /health, POST /v1/resize (JSON or multipart)
pm-image serve --host 127.0.0.1 -p 8080
Multipart (response = image bytes):
curl -s -o thumb.webp -X POST http://127.0.0.1:8080/v1/resize \
-F "file=@/path/in.png" \
-F "max_width=400" \
-F "format=webp"
JSON (paths must be readable/writable by the server process):
curl -s http://127.0.0.1:8080/health
curl -s -X POST http://127.0.0.1:8080/v1/resize \
-H 'Content-Type: application/json' \
-d '{"input":"/path/in.png","output":"/path/out.webp","max_width":400,"quality":80}'
Optional JSON: "cache":false, "expand_glob":false, "cache_dir":"..." — see Batch paths & cache in the README.
ipc — one JSON line per connection (TCP; Unix socket on Linux/macOS)
pm-image ipc --host 127.0.0.1 -p 9333 --cache-dir ./cache/images
# elsewhere: send a single line, read one line back, e.g.
# {"input":"/tmp/a.jpg","output":"/tmp/b.webp","max_width":320,"format":"webp","cache":true}
Same JSON fields as REST (input, output, globs, expand_glob, cache, cache_dir, resize options).