253 lines
12 KiB
Markdown
253 lines
12 KiB
Markdown
# 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](https://service.polymech.info/api/vfs/get/software/pm-image-resize/v1.zip?t=1776153671416&download=1) | [Source Code](https://git.polymech.info/polymech/mono/src/branch/master/packages/media)
|
||
|
||
|
||
## 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/resize` as **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).
|
||
|
||
```bash
|
||
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 `--format` when 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):
|
||
|
||
```bash
|
||
# 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.
|
||
|
||
```bash
|
||
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`): **`input`** and **`output`** paths 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 optionally `count` / `outputs` for batches).
|
||
- **Multipart upload** (`Content-Type: multipart/form-data`): send the image in a part named **`file`**, **`image`**, or **`upload`**. Add optional fields (`max_width`, `format`, …) as additional form parts. The **response body is the encoded image** (not JSON), with `Content-Type` set from the output format (default **JPEG** if `format` is omitted). Uploads do not use the on-disk output cache.
|
||
|
||
See the **[C++ README](../README.md)** (`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`/`output` paths — 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](../README.md)**.
|
||
|
||
---
|
||
|
||
## 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`](https://www.libvips.org/API/current/class.Image.html).
|
||
---
|
||
|
||
## 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
|
||
|
||
```bash
|
||
pm-image --help
|
||
pm-image resize --help
|
||
pm-image -v
|
||
```
|
||
|
||
### `resize` — fit inside a box (default), write WebP / AVIF by extension
|
||
|
||
```bash
|
||
# 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). |
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
# 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
|
||
|
||
```bash
|
||
# 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):
|
||
|
||
```bash
|
||
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):
|
||
|
||
```bash
|
||
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](../README.md).
|
||
|
||
### `ipc` — one JSON line per connection (TCP; Unix socket on Linux/macOS)
|
||
|
||
```bash
|
||
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).
|
||
|