docs(api): achieve 100% endpoint coverage in OpenAPI spec and VitePress docs (#54)

Add 12 previously undocumented routes to the OpenAPI 3.1 specification:
content-aware-resize, edit-metadata (+ inspect), stitch, pdf-to-image
(+ info, preview), gif-tools/info, remove-background/effects, preview,
pipeline/tools, and pipeline/batch. Fix license from MIT to AGPL-3.0,
correct DELETE /files response from 204 to 200 with body, and update
VitePress API docs (rest.md tool table, ai.md model parameters). Also
register the sharpen operation in the image-engine OPERATION_MAP so it
can be used as a standalone pipeline step.

Co-authored-by: stirling-image <stirling-image@users.noreply.github.com>
This commit is contained in:
stirling-image
2026-04-13 17:30:08 +08:00
committed by GitHub
co-authored by stirling-image
parent cd886f0f82
commit 34ec840b72
4 changed files with 654 additions and 21 deletions
+588 -4
View File
@@ -15,7 +15,7 @@ info:
Endpoints marked with a lock icon require authentication. Admin-only endpoints are noted in their description.
license:
name: MIT
name: AGPL-3.0
url: https://github.com/stirling-image/stirling-image/blob/main/LICENSE
servers:
@@ -158,6 +158,60 @@ paths:
"401":
description: Authentication required
/api/v1/tools/content-aware-resize:
post:
tags: [Tools]
summary: Content-aware resize
description: Resize an image using seam carving to intelligently remove or insert content while preserving important features.
security:
- bearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
description: Image file to process
settings:
type: string
description: |
JSON string with options:
- `width` (number, optional) — Target width in pixels
- `height` (number, optional) — Target height in pixels
- `protectFaces` (boolean, default false) — Detect and protect faces from distortion
- `blurRadius` (number 0-20, default 4) — Blur radius for energy map
- `sobelThreshold` (number 1-20, default 2) — Edge detection threshold
- `square` (boolean, default false) — Crop to square aspect ratio
At least one of `width`, `height`, or `square` must be provided.
responses:
"200":
description: Processed image
content:
application/json:
schema:
allOf:
- $ref: "#/components/schemas/ToolResponse"
- type: object
properties:
width:
type: integer
height:
type: integer
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Authentication required
/api/v1/tools/crop:
post:
tags: [Tools]
@@ -776,6 +830,59 @@ paths:
"401":
description: Authentication required
/api/v1/tools/gif-tools/info:
post:
tags: [Tools]
summary: GIF info
description: Get metadata about an animated GIF including dimensions, frame count, delays, and duration.
security: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
description: GIF file to inspect
responses:
"200":
description: GIF metadata
content:
application/json:
schema:
type: object
properties:
width:
type: integer
height:
type: integer
pages:
type: integer
description: Number of frames
delay:
type: array
items:
type: integer
description: Per-frame delay in milliseconds
loop:
type: integer
description: Loop count (0 = infinite)
fileSize:
type: integer
duration:
type: integer
description: Total duration in milliseconds
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/api/v1/tools/smart-crop:
post:
tags: [Tools]
@@ -1005,6 +1112,162 @@ paths:
"401":
description: Authentication required
/api/v1/tools/pdf-to-image:
post:
tags: [Tools]
summary: PDF to image
description: Convert PDF pages to images. Returns individual page downloads and a ZIP of all pages.
security:
- bearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
description: PDF file to convert
settings:
type: string
description: |
JSON string with options:
- `format` (string, default "png") — Output format: png, jpg, webp, avif, tiff, gif, heic, heif
- `dpi` (number 36-1200, default 150) — Resolution in dots per inch
- `quality` (number 1-100, default 85) — Output quality
- `colorMode` (string, default "color") — One of: color, grayscale, bw
- `pages` (string, default "all") — Page selection, e.g. "all", "1-3", "1,3,5"
responses:
"200":
description: Converted pages
content:
application/json:
schema:
type: object
properties:
jobId:
type: string
pageCount:
type: integer
description: Total pages in the PDF
selectedPages:
type: array
items:
type: integer
format:
type: string
pages:
type: array
items:
type: object
properties:
page:
type: integer
downloadUrl:
type: string
size:
type: integer
zipUrl:
type: string
description: URL to download all pages as ZIP
zipSize:
type: integer
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Authentication required
/api/v1/tools/pdf-to-image/info:
post:
tags: [Tools]
summary: PDF page count
description: Get the number of pages in a PDF file.
security: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
description: PDF file to inspect
responses:
"200":
description: PDF info
content:
application/json:
schema:
type: object
properties:
pageCount:
type: integer
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/api/v1/tools/pdf-to-image/preview:
post:
tags: [Tools]
summary: PDF page thumbnails
description: Generate thumbnail previews for each page in a PDF (max 200 pages, JPEG 300px wide).
security: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
description: PDF file to preview
responses:
"200":
description: Page thumbnails
content:
application/json:
schema:
type: object
properties:
pageCount:
type: integer
thumbnails:
type: array
items:
type: object
properties:
page:
type: integer
dataUrl:
type: string
description: Base64-encoded JPEG data URL
width:
type: integer
height:
type: integer
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/api/v1/tools/split:
post:
tags: [Tools]
@@ -1342,6 +1605,57 @@ paths:
"401":
description: Authentication required
/api/v1/tools/stitch:
post:
tags: [Tools]
summary: Stitch images
description: Join multiple images horizontally, vertically, or in a grid layout.
security:
- bearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: array
items:
type: string
format: binary
description: Two or more image files to stitch together
settings:
type: string
description: |
JSON string with options:
- `direction` (string, default "horizontal") — One of: horizontal, vertical, grid
- `gridColumns` (integer 2-10, default 2) — Columns when direction is grid
- `resizeMode` (string, default "fit") — One of: fit, original, stretch, crop
- `alignment` (string, default "center") — One of: start, center, end
- `gap` (number 0-200, default 0) — Gap between images in pixels
- `border` (number 0-50, default 0) — Border width in pixels
- `cornerRadius` (number 0-50, default 0) — Corner radius in pixels
- `backgroundColor` (string, default "#FFFFFF") — Hex color for background and gap fill
- `format` (string, default "png") — One of: png, jpeg, webp
- `quality` (number 1-100, default 90) — Output quality
responses:
"200":
description: Stitched image
content:
application/json:
schema:
$ref: "#/components/schemas/ToolResponse"
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Authentication required
/api/v1/tools/remove-background:
post:
tags: [Tools]
@@ -1383,6 +1697,65 @@ paths:
"401":
description: Authentication required
/api/v1/tools/remove-background/effects:
post:
tags: [Tools]
summary: Apply background effects
description: >
Apply background replacement or effects to a previously processed remove-background result.
Requires a jobId from a prior POST /api/v1/tools/remove-background call.
security:
- bearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [settings]
properties:
backgroundImage:
type: string
format: binary
description: Custom background image (required when backgroundType is "image")
settings:
type: string
description: |
JSON string with options:
- `jobId` (string, required) — Job ID from the initial remove-background call
- `filename` (string, required) — Original filename
- `backgroundType` (string) — One of: transparent, color, gradient, blur, image
- `backgroundColor` (string) — Hex color for solid background
- `gradientColor1` (string) — First gradient color
- `gradientColor2` (string) — Second gradient color
- `gradientAngle` (number) — Gradient angle in degrees
- `blurEnabled` (boolean) — Enable background blur effect
- `blurIntensity` (number 0-100) — Blur strength
- `shadowEnabled` (boolean) — Enable drop shadow
- `shadowOpacity` (number 0-100) — Shadow opacity
responses:
"200":
description: Image with applied effects
content:
application/json:
schema:
type: object
properties:
jobId:
type: string
downloadUrl:
type: string
processedSize:
type: integer
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Authentication required
/api/v1/tools/upscale:
post:
tags: [Tools]
@@ -1800,6 +2173,98 @@ paths:
"401":
description: Authentication required
/api/v1/tools/edit-metadata:
post:
tags: [Tools]
summary: Edit metadata
description: Edit EXIF, IPTC, GPS, and other metadata fields in an image using ExifTool.
security:
- bearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
description: Image file to process
settings:
type: string
description: |
JSON string with options:
- `artist` (string) — Artist or author name
- `copyright` (string) — Copyright notice
- `imageDescription` (string) — Image description
- `software` (string) — Software used
- `dateTime` (string) — Date/time string
- `dateTimeOriginal` (string) — Original capture date/time
- `clearGps` (boolean, default false) — Remove all GPS data
- `fieldsToRemove` (string[]) — Specific metadata fields to remove
- `gpsLatitude` (number -90 to 90) — GPS latitude
- `gpsLongitude` (number -180 to 180) — GPS longitude
- `gpsAltitude` (number) — GPS altitude in meters
- `keywords` (string[]) — Keywords or tags
- `keywordsMode` (string, default "add") — One of: add, set
- `dateShift` (string) — Shift dates by offset, e.g. "+2:00" or "-1:30"
- `setAllDates` (string) — Set all date fields to this value
- `iptcTitle` (string) — IPTC title
- `iptcHeadline` (string) — IPTC headline
- `iptcCity` (string) — IPTC city
- `iptcState` (string) — IPTC state or province
- `iptcCountry` (string) — IPTC country
responses:
"200":
description: Image with updated metadata
content:
application/json:
schema:
$ref: "#/components/schemas/ToolResponse"
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Authentication required
/api/v1/tools/edit-metadata/inspect:
post:
tags: [Tools]
summary: Inspect metadata (ExifTool)
description: Read all metadata from an image using ExifTool. Returns all embedded EXIF, IPTC, XMP, and GPS fields.
security: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
description: Image file to inspect
responses:
"200":
description: Full metadata object (keys vary by image)
content:
application/json:
schema:
type: object
additionalProperties: true
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
# ─── Batch ────────────────────────────────────────────────────────────────
/api/v1/tools/{toolId}/batch:
@@ -2017,6 +2482,82 @@ paths:
"401":
description: Authentication required
/api/v1/pipeline/tools:
get:
tags: [Pipelines]
summary: List pipeline-compatible tools
description: Returns all tool IDs that can be used as pipeline steps.
security: []
responses:
"200":
description: Tool IDs
content:
application/json:
schema:
type: object
properties:
toolIds:
type: array
items:
type: string
/api/v1/pipeline/batch:
post:
tags: [Pipelines]
summary: Run pipeline on multiple files
description: Execute a pipeline across multiple images. Returns a ZIP file with all processed results.
security:
- bearerAuth: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file, pipeline]
properties:
file:
type: array
items:
type: string
format: binary
description: Image files to process
pipeline:
type: string
description: |
JSON string with pipeline definition:
- `steps` (array, required, 1-20 items) — Pipeline steps
- `steps[].toolId` (string, required) — Tool ID for this step
- `steps[].settings` (object, optional) — Tool-specific settings
clientJobId:
type: string
description: Optional client-provided job ID for progress tracking via SSE
responses:
"200":
description: ZIP file with processed images
headers:
X-Job-Id:
schema:
type: string
description: Job ID for progress tracking
X-File-Results:
schema:
type: string
description: JSON mapping of input index to output filename
content:
application/zip:
schema:
type: string
format: binary
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
"401":
description: Authentication required
# ─── Files ────────────────────────────────────────────────────────────────
/api/v1/upload:
@@ -2084,6 +2625,39 @@ paths:
"404":
description: File not found
/api/v1/preview:
post:
tags: [Files]
summary: Generate image preview
description: Convert any image (including HEIC/HEIF) to a WebP preview, resized to fit within 1200x1200 pixels.
security: []
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required: [file]
properties:
file:
type: string
format: binary
description: Image file to preview
responses:
"200":
description: WebP preview image
content:
image/webp:
schema:
type: string
format: binary
"400":
description: Invalid input
content:
application/json:
schema:
$ref: "#/components/schemas/Error"
/api/v1/files:
get:
tags: [Files]
@@ -2124,7 +2698,8 @@ paths:
description: Authentication required
delete:
tags: [Files]
summary: Delete saved files
summary: Bulk delete saved files
description: Delete files and their entire version chains. Non-admin users can only delete their own files.
security:
- bearerAuth: []
requestBody:
@@ -2139,9 +2714,18 @@ paths:
type: array
items:
type: string
description: File IDs to delete (deletes entire version chain for each)
responses:
"204":
description: Files deleted
"200":
description: Deletion result
content:
application/json:
schema:
type: object
properties:
deleted:
type: integer
description: Total number of records deleted across all version chains
"400":
description: Invalid input
content:
+55 -11
View File
@@ -12,26 +12,33 @@ The Docker image includes CUDA-accelerated ML libraries on amd64. Add `--gpus al
Removes the background from an image and returns a transparent PNG.
**Model:** BiRefNet-Lite via [rembg](https://github.com/danielgatis/rembg)
**Model:** BiRefNet models via [rembg](https://github.com/danielgatis/rembg)
| Parameter | Type | Description |
|---|---|---|
| `model` | string | Model name. Default: `birefnet-lite`. Options include `u2net`, `isnet-general-use`, and others supported by rembg. |
| `model` | string | Model name. Default: `birefnet-general`. Available models: `birefnet-general`, `birefnet-general-lite`, `birefnet-matting`, `birefnet-portrait`, `bria-rmbg`, `u2net`. |
| `alphaMatting` | boolean | Use alpha matting for finer edge detail |
| `alphaMattingForegroundThreshold` | number | Foreground threshold for alpha matting (0-255) |
| `alphaMattingBackgroundThreshold` | number | Background threshold for alpha matting (0-255) |
A Phase 2 effects endpoint is also available at `POST /api/v1/tools/remove-background/effects`. After removing the background, you can apply post-processing effects such as replacement backgrounds, blur, and drop shadows.
**Python script:** `packages/ai/python/remove_bg.py`
## Upscaling
Increases image resolution using AI super-resolution.
**Model:** [RealESRGAN](https://github.com/xinntao/Real-ESRGAN)
**Model:** [RealESRGAN](https://github.com/xinntao/Real-ESRGAN) with Lanczos fallback
| Parameter | Type | Description |
|---|---|---|
| `scale` | number | Upscale factor: `2` or `4` |
| `scale` | number | Upscale factor (2-8) |
| `model` | string | Model selection: `auto`, `realesrgan`, or `lanczos`. Default: `auto`. |
| `faceEnhance` | boolean | Enable face enhancement for better facial detail |
| `denoise` | number | Denoise strength (0-1). Higher values remove more noise but may lose detail. |
| `format` | string | Output format (e.g. `png`, `webp`, `jpeg`) |
| `quality` | number | Output quality for lossy formats |
Returns the upscaled image along with the original and new dimensions.
@@ -39,13 +46,18 @@ Returns the upscaled image along with the original and new dimensions.
## OCR (text recognition)
Extracts text from images.
Extracts text from images. Three quality tiers are available, each trading speed for accuracy.
**Model:** [PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR)
**Models:**
- `fast` - [Tesseract](https://github.com/tesseract-ocr/tesseract) for quick extraction
- `balanced` - [PaddleOCR](https://github.com/PaddlePaddle/PaddleOCR) PP-OCRv5 for general-purpose use
- `best` - PaddleOCR-VL 1.5 vision-language model for maximum accuracy
| Parameter | Type | Description |
|---|---|---|
| `language` | string | Language code (e.g. `en`, `ch`, `fr`, `de`) |
| `quality` | string | Quality tier: `fast`, `balanced`, or `best`. Default: `balanced`. |
| `language` | string | Language code: `auto`, `en`, `de`, `fr`, `es`, `zh`, `ja`, `ko`. Default: `auto`. |
| `enhance` | boolean | Apply image preprocessing to improve recognition accuracy |
Returns structured results with text content, bounding boxes, and confidence scores for each detected text region.
@@ -59,7 +71,8 @@ Detects faces in an image and applies a blur to each detected region.
| Parameter | Type | Description |
|---|---|---|
| `blurStrength` | number | How strongly to blur detected faces |
| `blurRadius` | number | Blur radius for detected faces (1-100). Default: `30`. |
| `sensitivity` | number | Detection sensitivity (0-1). Lower values require higher confidence. Default: `0.5`. |
Returns the blurred image along with metadata about each detected face region (bounding box coordinates and confidence score).
@@ -69,12 +82,41 @@ Returns the blurred image along with metadata about each detected face region (b
Removes objects from images by filling in the area with generated content that matches the surroundings.
**Model:** OpenCV TELEA algorithm
**Model:** [LaMa](https://github.com/advimman/lama) (Large Mask Inpainting) via ONNX Runtime
Takes an image and a mask (white = area to erase, black = keep). Returns the inpainted image.
Takes an image and a mask file (white = area to erase, black = keep). Returns the inpainted image. GPU acceleration is available via ONNX CUDAExecutionProvider when a compatible GPU is detected.
**Python script:** `packages/ai/python/inpaint.py`
## Content-aware resize (seam carving)
Intelligently resizes images by removing or inserting seams - paths of least visual importance. This preserves the main subject and structure of the image while changing its dimensions.
**Engine:** `caire` Go binary
| Parameter | Type | Description |
|---|---|---|
| `width` | number | Target width in pixels |
| `height` | number | Target height in pixels |
| `protectFaces` | boolean | Use face detection to protect facial regions from seam removal |
| `blurRadius` | number | Gaussian blur radius for energy map computation (0-20) |
| `sobelThreshold` | number | Edge detection threshold for energy computation (1-20) |
| `square` | boolean | Force output to a square aspect ratio |
## Smart crop
Automatically crops images to focus on the most important region. Combines Sharp attention/entropy strategies with MediaPipe face detection to find the optimal crop area.
**Models:** Sharp (attention/entropy) + [MediaPipe](https://github.com/google/mediapipe) Face Detection
Three modes are available:
- **subject** - Uses Sharp's attention strategy to find the most visually interesting region.
- **face** - Uses MediaPipe face detection to center the crop on detected faces.
- **trim** - Removes uniform borders and whitespace from the edges of the image.
Parameters vary by mode. See the interactive API reference at `/api/docs` for the full parameter list for each mode.
## How the bridge works
The TypeScript bridge (`packages/ai/src/bridge.ts`) exposes a single function, `runPythonWithProgress`, that does the following for each AI call:
@@ -86,6 +128,8 @@ The TypeScript bridge (`packages/ai/src/bridge.ts`) exposes a single function, `
5. Reads the output image from the filesystem.
6. Cleans up temp files.
The persistent dispatcher pre-imports rembg, OpenCV, NumPy, and Pillow at startup. This means the first AI call after container start is fast instead of waiting for library imports. The dispatcher handles requests sequentially (Python's GIL) and reports readiness via a `{"ready": true}` message on stderr.
The persistent dispatcher pre-imports rembg, torch, PaddleOCR, MediaPipe, and the LaMa ONNX model at startup. This means the first AI call after container start is fast instead of waiting for library imports. The dispatcher handles requests sequentially (Python's GIL) and reports readiness via a `{"ready": true}` message on stderr.
GPU detection is handled by `packages/ai/python/gpu.py`, which checks for CUDA availability at startup and configures each model to use GPU or CPU accordingly.
If the Python process exits with a non-zero code, the bridge extracts a user-friendly error from stderr/stdout and throws. Timeouts default to 5 minutes.
+8 -6
View File
@@ -3,7 +3,7 @@
The API server runs on port 1349 by default and serves all endpoints under `/api`.
::: tip Full API Reference
Your Stirling Image instance includes a complete interactive API reference at `/api/docs` (e.g. `http://your-host:1349/api/docs`) with all 67 endpoints, request/response schemas, and examples.
Your Stirling Image instance includes a complete interactive API reference at `/api/docs` (e.g. `http://your-host:1349/api/docs`) with all 80+ endpoints, request/response schemas, and examples.
:::
::: info LLM-friendly docs
@@ -48,13 +48,13 @@ Send a multipart request with:
| Category | Tools |
|----------|-------|
| **Essentials** | `resize`, `crop`, `rotate`, `convert`, `compress` |
| **Optimization** | `strip-metadata`, `bulk-rename`, `image-to-pdf`, `favicon` |
| **Adjustments** | `brightness-contrast`, `saturation`, `color-channels`, `color-effects`, `replace-color` |
| **AI** | `remove-background`, `upscale`, `erase-object`, `ocr`, `blur-faces`, `smart-crop` |
| **Optimization** | `strip-metadata`, `edit-metadata`, `bulk-rename`, `image-to-pdf`, `favicon` |
| **Adjustments** | `adjust-colors`, `replace-color` |
| **AI** | `remove-background`, `upscale`, `erase-object`, `ocr`, `blur-faces`, `smart-crop`, `content-aware-resize` |
| **Watermark** | `watermark-text`, `watermark-image`, `text-overlay`, `compose` |
| **Utilities** | `info`, `compare`, `find-duplicates`, `color-palette`, `qr-generate`, `barcode-read` |
| **Layout** | `collage`, `split`, `border` |
| **Format** | `svg-to-raster`, `vectorize`, `gif-tools` |
| **Layout** | `collage`, `split`, `border`, `stitch` |
| **Format** | `svg-to-raster`, `vectorize`, `gif-tools`, `pdf-to-image` |
Each tool's specific settings are documented in the interactive API reference at `/api/docs` on your running instance.
@@ -72,8 +72,10 @@ Chain tools into reusable workflows.
```
POST /api/v1/pipeline/execute -- Run a pipeline (multipart: file + steps JSON)
POST /api/v1/pipeline/batch -- Run a pipeline across multiple files (returns ZIP)
POST /api/v1/pipeline/save -- Save a named pipeline
GET /api/v1/pipeline/list -- List saved pipelines
GET /api/v1/pipeline/tools -- List all pipeline-compatible tool IDs
DELETE /api/v1/pipeline/:id -- Delete a pipeline
```
+3
View File
@@ -13,6 +13,7 @@ import { resize } from "./operations/resize.js";
import { rotate } from "./operations/rotate.js";
import { saturation } from "./operations/saturation.js";
import { sepia } from "./operations/sepia.js";
import { sharpen } from "./operations/sharpen.js";
import { stripMetadata } from "./operations/strip-metadata.js";
import type {
BrightnessOptions,
@@ -29,6 +30,7 @@ import type {
RotateOptions,
SaturationOptions,
Sharp,
SharpenOptions,
StripMetadataOptions,
} from "./types.js";
import { getImageInfo } from "./utils/metadata.js";
@@ -55,6 +57,7 @@ const OPERATION_MAP: Record<
"color-channels": (img, opts) => colorChannels(img, opts as unknown as ColorChannelOptions),
grayscale: (img) => grayscale(img),
sepia: (img) => sepia(img),
sharpen: (img, opts) => sharpen(img, opts as unknown as SharpenOptions),
invert: (img) => invert(img),
"edit-metadata": (img, opts) => editMetadata(img, opts as unknown as EditMetadataOptions),
};