diff --git a/apps/api/src/openapi.yaml b/apps/api/src/openapi.yaml index 47abe637..fe54c623 100644 --- a/apps/api/src/openapi.yaml +++ b/apps/api/src/openapi.yaml @@ -4300,6 +4300,12 @@ paths: application/json: schema: $ref: "#/components/schemas/UnauthorizedError" + "422": + description: Failed to read image metadata + content: + application/json: + schema: + $ref: "#/components/schemas/Error" /api/v1/tools/edit-metadata: post: @@ -4373,7 +4379,8 @@ paths: 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: [] + security: + - bearerAuth: [] requestBody: required: true content: @@ -4400,6 +4407,18 @@ paths: application/json: schema: $ref: "#/components/schemas/Error" + "401": + description: Authentication required + content: + application/json: + schema: + $ref: "#/components/schemas/UnauthorizedError" + "422": + description: Failed to read image metadata + content: + application/json: + schema: + $ref: "#/components/schemas/Error" # ─── Batch ──────────────────────────────────────────────────────────────── diff --git a/apps/api/src/routes/tools/color-adjustments.ts b/apps/api/src/routes/tools/adjust-colors.ts similarity index 97% rename from apps/api/src/routes/tools/color-adjustments.ts rename to apps/api/src/routes/tools/adjust-colors.ts index 848c74c1..920ed0f1 100644 --- a/apps/api/src/routes/tools/color-adjustments.ts +++ b/apps/api/src/routes/tools/adjust-colors.ts @@ -35,8 +35,8 @@ const settingsSchema = z.object({ /** * Build a 3x3 recomb matrix for color temperature + tint shift. - * Temperature: cool (blue) ←→ warm (orange) on the blue-orange axis. - * Tint: green ←→ magenta on the green-magenta axis. + * Temperature: cool (blue) <-> warm (orange) on the blue-orange axis. + * Tint: green <-> magenta on the green-magenta axis. */ function colorTempTintMatrix( temp: number, diff --git a/apps/api/src/routes/tools/index.ts b/apps/api/src/routes/tools/index.ts index 5654ff4a..cd08d4c2 100644 --- a/apps/api/src/routes/tools/index.ts +++ b/apps/api/src/routes/tools/index.ts @@ -2,6 +2,7 @@ import { TOOLS } from "@snapotter/shared"; import { eq } from "drizzle-orm"; import type { FastifyInstance } from "fastify"; import { db, schema } from "../../db/index.js"; +import { registerColorAdjustments } from "./adjust-colors.js"; import { registerAiCanvasExpand } from "./ai-canvas-expand.js"; import { registerBarcodeRead } from "./barcode-read.js"; import { registerBeautify } from "./beautify.js"; @@ -9,7 +10,6 @@ import { registerBlurFaces } from "./blur-faces.js"; import { registerBorder } from "./border.js"; import { registerBulkRename } from "./bulk-rename.js"; import { registerCollage } from "./collage.js"; -import { registerColorAdjustments } from "./color-adjustments.js"; import { registerColorBlindness } from "./color-blindness.js"; import { registerColorPalette } from "./color-palette.js"; import { registerColorize } from "./colorize.js"; diff --git a/apps/docs/.vitepress/config.mts b/apps/docs/.vitepress/config.mts index f6f81456..52a8f48d 100644 --- a/apps/docs/.vitepress/config.mts +++ b/apps/docs/.vitepress/config.mts @@ -5,7 +5,7 @@ import pkg from "../package.json"; export default defineConfig({ title: "SnapOtter", description: - "Documentation for SnapOtter - A Self Hosted Image Manipulator. 52 tools, local AI, pipelines, REST API.", + "Documentation for SnapOtter - A Self Hosted Image Manipulator. 53 tools, local AI, pipelines, REST API.", base: "/", appearance: { initialValue: "light" }, srcDir: ".", @@ -76,7 +76,7 @@ export default defineConfig({ `, customTemplateVariables: { description: - "SnapOtter is a self-hosted, open-source image processing platform with 52 tools including AI/ML. Runs in a single Docker container with GPU auto-detection.", + "SnapOtter is a self-hosted, open-source image processing platform with 53 tools including AI/ML. Runs in a single Docker container with GPU auto-detection.", details: "Resize, compress, convert, remove backgrounds, upscale, run OCR, and more - without sending images to external services.", }, @@ -90,6 +90,7 @@ export default defineConfig({ nav: [ { text: "Home", link: "/" }, { text: "Guide", link: "/guide/getting-started" }, + { text: "Tools", link: "/tools/resize" }, { text: "API Reference", link: "/api/rest" }, { text: "Changelog", link: "/changelog" }, { @@ -116,6 +117,104 @@ export default defineConfig({ { text: "Contributing", link: "/guide/contributing" }, ], }, + { + text: "Tools", + items: [ + { + text: "Essentials", + items: [ + { text: "Resize", link: "/tools/resize" }, + { text: "Crop", link: "/tools/crop" }, + { text: "Rotate & Flip", link: "/tools/rotate" }, + { text: "Convert", link: "/tools/convert" }, + { text: "Compress", link: "/tools/compress" }, + ], + }, + { + text: "Optimization", + items: [ + { text: "Optimize for Web", link: "/tools/optimize-for-web" }, + { text: "Remove Metadata", link: "/tools/strip-metadata" }, + { text: "Edit Metadata", link: "/tools/edit-metadata" }, + { text: "Bulk Rename", link: "/tools/bulk-rename" }, + { text: "Image to PDF", link: "/tools/image-to-pdf" }, + { text: "Favicon Generator", link: "/tools/favicon" }, + ], + }, + { + text: "Adjustments", + items: [ + { text: "Adjust Colors", link: "/tools/adjust-colors" }, + { text: "Sharpening", link: "/tools/sharpening" }, + { text: "Replace & Invert Color", link: "/tools/replace-color" }, + { text: "Color Blindness Simulation", link: "/tools/color-blindness" }, + ], + }, + { + text: "Watermark & Overlay", + items: [ + { text: "Text Watermark", link: "/tools/watermark-text" }, + { text: "Image Watermark", link: "/tools/watermark-image" }, + { text: "Text Overlay", link: "/tools/text-overlay" }, + { text: "Image Composition", link: "/tools/compose" }, + { text: "Meme Generator", link: "/tools/meme-generator" }, + ], + }, + { + text: "Utilities", + items: [ + { text: "Image Info", link: "/tools/info" }, + { text: "Image Compare", link: "/tools/compare" }, + { text: "Find Duplicates", link: "/tools/find-duplicates" }, + { text: "Color Palette", link: "/tools/color-palette" }, + { text: "QR Code Generator", link: "/tools/qr-generate" }, + { text: "HTML to Image", link: "/tools/html-to-image" }, + { text: "Barcode Reader", link: "/tools/barcode-read" }, + { text: "Image to Base64", link: "/tools/image-to-base64" }, + ], + }, + { + text: "Layout", + items: [ + { text: "Collage / Grid", link: "/tools/collage" }, + { text: "Stitch / Combine", link: "/tools/stitch" }, + { text: "Image Splitting", link: "/tools/split" }, + { text: "Border & Frame", link: "/tools/border" }, + { text: "Beautify Screenshot", link: "/tools/beautify" }, + ], + }, + { + text: "Format", + items: [ + { text: "SVG to Raster", link: "/tools/svg-to-raster" }, + { text: "Image to SVG", link: "/tools/vectorize" }, + { text: "GIF Tools", link: "/tools/gif-tools" }, + { text: "PDF to Image", link: "/tools/pdf-to-image" }, + ], + }, + { + text: "AI Tools", + items: [ + { text: "Remove Background", link: "/tools/remove-background" }, + { text: "Image Upscaling", link: "/tools/upscale" }, + { text: "Object Eraser", link: "/tools/erase-object" }, + { text: "OCR / Text Extraction", link: "/tools/ocr" }, + { text: "Face / PII Blur", link: "/tools/blur-faces" }, + { text: "Smart Crop", link: "/tools/smart-crop" }, + { text: "Image Enhancement", link: "/tools/image-enhancement" }, + { text: "Face Enhancement", link: "/tools/enhance-faces" }, + { text: "AI Colorization", link: "/tools/colorize" }, + { text: "Noise Removal", link: "/tools/noise-removal" }, + { text: "Red Eye Removal", link: "/tools/red-eye-removal" }, + { text: "Photo Restoration", link: "/tools/restore-photo" }, + { text: "Passport Photo", link: "/tools/passport-photo" }, + { text: "Content-Aware Resize", link: "/tools/content-aware-resize" }, + { text: "AI Canvas Expand", link: "/tools/ai-canvas-expand" }, + { text: "PNG Transparency Fixer", link: "/tools/transparency-fixer" }, + ], + }, + ], + }, { text: "API reference", items: [ diff --git a/apps/docs/tools/adjust-colors.md b/apps/docs/tools/adjust-colors.md new file mode 100644 index 00000000..aca2b424 --- /dev/null +++ b/apps/docs/tools/adjust-colors.md @@ -0,0 +1,68 @@ +--- +description: Adjust brightness, contrast, saturation, temperature, hue, channels, and apply color effects. +--- + +# Adjust Colors + +Comprehensive color adjustment tool combining brightness, contrast, exposure, saturation, temperature, tint, hue rotation, per-channel levels, and one-click effects (grayscale, sepia, invert) in a single endpoint. + +## API Endpoint + +`POST /api/v1/tools/adjust-colors` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| brightness | number | No | `0` | Brightness adjustment (-100 to 100) | +| contrast | number | No | `0` | Contrast adjustment (-100 to 100) | +| exposure | number | No | `0` | Exposure / midtone gamma (-100 to 100) | +| saturation | number | No | `0` | Color saturation (-100 to 100) | +| temperature | number | No | `0` | White balance: cool/blue to warm/orange (-100 to 100) | +| tint | number | No | `0` | Tint shift: green to magenta (-100 to 100) | +| hue | number | No | `0` | Hue rotation in degrees (-180 to 180) | +| sharpness | number | No | `0` | Sharpening strength (0 to 100) | +| red | number | No | `100` | Red channel level (0 to 200, 100 = unchanged) | +| green | number | No | `100` | Green channel level (0 to 200, 100 = unchanged) | +| blue | number | No | `100` | Blue channel level (0 to 200, 100 = unchanged) | +| effect | string | No | `"none"` | Color effect: `none`, `grayscale`, `sepia`, `invert` | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/adjust-colors \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"brightness": 20, "contrast": 10, "saturation": -30, "effect": "none"}' +``` + +Apply a warm vintage look: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/adjust-colors \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"temperature": 40, "saturation": -15, "contrast": 10, "effect": "sepia"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 2380000 +} +``` + +## Notes + +- All parameters default to neutral values so you can adjust only what you need. +- Adjustments are applied in this order: brightness, contrast, exposure, saturation/hue, temperature/tint, sharpness, channels, effects. +- Temperature uses a 3x3 color recombination matrix on the blue-orange and green-magenta axes. +- Exposure maps to Sharp's gamma function (positive brightens midtones, negative darkens them). +- This endpoint also responds at the legacy paths `/api/v1/tools/brightness-contrast`, `/api/v1/tools/saturation`, `/api/v1/tools/color-channels`, and `/api/v1/tools/color-effects`. +- Output format matches the input format. HEIC, RAW, PSD, and SVG inputs are automatically decoded before processing. diff --git a/apps/docs/tools/ai-canvas-expand.md b/apps/docs/tools/ai-canvas-expand.md new file mode 100644 index 00000000..daa846f5 --- /dev/null +++ b/apps/docs/tools/ai-canvas-expand.md @@ -0,0 +1,77 @@ +# AI Canvas Expand + +Expand the canvas of an image with AI-powered fill (outpainting). Extends the image in any direction and fills the new areas with AI-generated content that matches the existing image. + +## API Endpoint + +`POST /api/v1/tools/ai-canvas-expand` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `object-eraser-colorize` (1-2 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| extendTop | integer | No | `0` | Pixels to extend at the top | +| extendRight | integer | No | `0` | Pixels to extend at the right | +| extendBottom | integer | No | `0` | Pixels to extend at the bottom | +| extendLeft | integer | No | `0` | Pixels to extend at the left | +| tier | string | No | `"balanced"` | Quality tier: `fast`, `balanced`, `high` | +| format | string | No | `"auto"` | Output format: `auto`, `png`, `jpg`, `jpeg`, `webp`, `tiff`, `gif`, `avif`, `heic`, `heif`, `jxl` | +| quality | integer | No | `95` | Output quality (1-100) | + +At least one extend direction must be greater than 0. + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/ai-canvas-expand \ + -F "file=@photo.jpg" \ + -F 'settings={"extendTop":200,"extendBottom":200,"extendLeft":100,"extendRight":100,"tier":"balanced"}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Expanding canvas...","percent":50} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/photo_extended.png", + "previewUrl": "/api/v1/download/{jobId}/preview.webp", + "originalSize": 300000, + "processedSize": 520000 + } +} +``` + +## Notes + +- Requires the `object-eraser-colorize` model bundle to be installed (1-2 GB). +- Uses LaMa-based outpainting to generate content for the expanded regions. +- The `tier` parameter trades speed for quality: `fast` produces results quickly with potential artifacts, `high` takes longer but produces smoother, more coherent fills. +- Extend values are in pixels. The final image dimensions will be: original width + extendLeft + extendRight by original height + extendTop + extendBottom. +- For non-browser-previewable output formats (HEIC, JXL, TIFF), a WebP preview is generated alongside the main output. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/barcode-read.md b/apps/docs/tools/barcode-read.md new file mode 100644 index 00000000..7c180cbd --- /dev/null +++ b/apps/docs/tools/barcode-read.md @@ -0,0 +1,94 @@ +--- +description: Scan images for QR codes, barcodes, and 2D codes with annotated output. +--- + +# Barcode Reader + +Scan uploaded images for all types of barcodes and QR codes. Returns decoded text, barcode type, and position data for each detected code. Also generates an annotated image with colored bounding boxes around detected codes. + +## API Endpoint + +`POST /api/v1/tools/barcode-read` + +Accepts multipart form data with an image file and an optional JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| tryHarder | boolean | No | `true` | Enable aggressive scanning mode for harder-to-read barcodes (slower but more thorough) | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/barcode-read \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@receipt.jpg" \ + -F 'settings={"tryHarder": true}' +``` + +## Example Response + +```json +{ + "filename": "receipt.jpg", + "barcodes": [ + { + "type": "QRCode", + "text": "https://example.com/product/123", + "position": { + "topLeft": { "x": 100, "y": 50 }, + "topRight": { "x": 250, "y": 50 }, + "bottomLeft": { "x": 100, "y": 200 }, + "bottomRight": { "x": 250, "y": 200 } + } + }, + { + "type": "EAN-13", + "text": "5901234123457", + "position": { + "topLeft": { "x": 50, "y": 400 }, + "topRight": { "x": 300, "y": 400 }, + "bottomLeft": { "x": 50, "y": 450 }, + "bottomRight": { "x": 300, "y": 450 } + } + } + ], + "annotatedUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/annotated-receipt.png", + "previewUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/annotated-receipt.png" +} +``` + +## Response Fields + +| Field | Type | Description | +|-------|------|-------------| +| filename | string | Original filename | +| barcodes | array | Array of detected barcode objects | +| annotatedUrl | string or null | URL to download the annotated image (null if no barcodes found) | +| previewUrl | string or null | Same as annotatedUrl (for frontend preview compatibility) | + +### Barcode Object + +| Field | Type | Description | +|-------|------|-------------| +| type | string | Barcode format (QRCode, EAN-13, Code128, DataMatrix, PDF417, etc.) | +| text | string | Decoded content of the barcode | +| position | object | Bounding box with topLeft, topRight, bottomLeft, bottomRight coordinates | + +## Supported Barcode Types + +1D barcodes: Code128, Code39, Code93, Codabar, EAN-8, EAN-13, ITF, UPC-A, UPC-E + +2D barcodes: QRCode, DataMatrix, PDF417, Aztec, MaxiCode + +## Notes + +- Uses the zxing-wasm library for barcode detection. +- The annotated image overlays colored polygon bounding boxes and numbered labels on each detected barcode. +- Up to 255 barcodes can be detected in a single image. +- If no barcodes are found, `barcodes` is an empty array and `annotatedUrl` is null. +- The `tryHarder` mode performs more thorough scanning at the cost of processing time. Disable it for faster processing of clean, well-aligned barcodes. +- The annotated output is always PNG format. +- HEIC, RAW, PSD, and SVG inputs are automatically decoded before scanning. +- EXIF orientation is auto-applied before processing. diff --git a/apps/docs/tools/beautify.md b/apps/docs/tools/beautify.md new file mode 100644 index 00000000..524fd944 --- /dev/null +++ b/apps/docs/tools/beautify.md @@ -0,0 +1,78 @@ +# Beautify Screenshot + +Add gradient backgrounds, device frames, shadows, watermarks, and social media sizing to screenshots. Ideal for creating polished images for product marketing, social media, and documentation. + +## API Endpoint + +`POST /api/v1/tools/beautify` + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| backgroundType | string | No | `"linear-gradient"` | Background type: `solid`, `linear-gradient`, `radial-gradient`, `image`, `transparent` | +| backgroundColor | string | No | `"#667eea"` | Solid background color (used when `backgroundType` is `solid`) | +| gradientStops | array | No | `[{"color":"#667eea","position":0},{"color":"#764ba2","position":100}]` | Gradient color stops (min 2). Each stop has `color` (hex) and `position` (0-100). | +| gradientAngle | number | No | 135 | Gradient angle in degrees (0 to 360) | +| padding | number | No | 64 | Padding around the image in pixels (0 to 256) | +| borderRadius | number | No | 12 | Corner radius on the screenshot (0 to 64) | +| shadowPreset | string | No | `"subtle"` | Shadow preset: `none`, `subtle`, `medium`, `dramatic`, `custom` | +| shadowBlur | number | No | 20 | Custom shadow blur radius (0 to 100, used when `shadowPreset` is `custom`) | +| shadowOffsetX | number | No | 0 | Custom shadow horizontal offset (-50 to 50) | +| shadowOffsetY | number | No | 10 | Custom shadow vertical offset (-50 to 50) | +| shadowColor | string | No | `"#000000"` | Custom shadow color as hex | +| shadowOpacity | number | No | 30 | Custom shadow opacity (0 to 100) | +| frame | string | No | `"none"` | Device or window frame: `none`, `macos-light`, `macos-dark`, `windows-light`, `windows-dark`, `browser-light`, `browser-dark`, `iphone`, `iphone-dark`, `macbook`, `macbook-dark`, `ipad`, `ipad-dark` | +| frameTitle | string | No | - | Title text displayed in window frame title bars | +| socialPreset | string | No | `"none"` | Resize to social media dimensions: `none`, `twitter`, `linkedin`, `instagram-square`, `instagram-story`, `facebook`, `producthunt` | +| watermarkText | string | No | - | Optional watermark text overlay | +| watermarkPosition | string | No | `"bottom-right"` | Watermark position: `top-left`, `top-right`, `bottom-left`, `bottom-right`, `center` | +| watermarkOpacity | number | No | 50 | Watermark opacity (0 to 100) | +| outputFormat | string | No | `"png"` | Output format: `png`, `jpeg`, `webp` | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/beautify \ + -F "file=@screenshot.png" \ + -F 'settings={"backgroundType":"linear-gradient","gradientStops":[{"color":"#667eea","position":0},{"color":"#764ba2","position":100}],"gradientAngle":135,"padding":64,"borderRadius":12,"shadowPreset":"medium","frame":"macos-dark","socialPreset":"twitter"}' +``` + +### With Background Image + +```bash +curl -X POST http://localhost:13490/api/v1/tools/beautify \ + -F "file=@screenshot.png" \ + -F "backgroundImage=@bg-texture.jpg" \ + -F 'settings={"backgroundType":"image","padding":80,"borderRadius":16,"shadowPreset":"dramatic"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/screenshot.png", + "originalSize": 234567, + "processedSize": 567890 +} +``` + +## Notes + +- Accepts two file fields: `file` (required, the main screenshot) and `backgroundImage` (optional, used when `backgroundType` is `image`). +- Supports HEIC, RAW, PSD, and SVG input formats (automatically decoded). +- Shadow presets map to specific values: + - `subtle`: blur 20, offsetY 4, opacity 20% + - `medium`: blur 40, offsetY 10, opacity 35% + - `dramatic`: blur 80, offsetY 20, opacity 50% +- Social media presets resize the final output to fit the target dimensions using `contain` mode: + - `twitter`: 1600x900 + - `linkedin`: 1200x627 + - `instagram-square`: 1080x1080 + - `instagram-story`: 1080x1920 + - `facebook`: 1200x630 + - `producthunt`: 1270x760 +- Device frames (`iphone`, `macbook`, `ipad`) apply a hardware bezel around the image and skip the `borderRadius` setting. +- When transparency is required (shadow, border radius, device frames, or transparent background), the output is forced to PNG even if `jpeg` is selected. +- Image backgrounds are not supported in pipeline/batch mode. diff --git a/apps/docs/tools/blur-faces.md b/apps/docs/tools/blur-faces.md new file mode 100644 index 00000000..be54ba72 --- /dev/null +++ b/apps/docs/tools/blur-faces.md @@ -0,0 +1,89 @@ +# Face / PII Blur + +Auto-detect and blur faces in images using AI-powered face detection (MediaPipe). + +## API Endpoint + +`POST /api/v1/tools/blur-faces` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `face-detection` (200-300 MB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| blurRadius | number | No | `30` | Blur radius applied to detected faces (1-100) | +| sensitivity | number | No | `0.5` | Face detection sensitivity (0-1). Lower values detect fewer faces with higher confidence | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/blur-faces \ + -F "file=@group-photo.jpg" \ + -F 'settings={"blurRadius":40,"sensitivity":0.3}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Detecting faces...","percent":40} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/group-photo_blurred.jpg", + "originalSize": 450000, + "processedSize": 420000, + "facesDetected": 3, + "faces": [ + {"x": 100, "y": 50, "w": 80, "h": 80}, + {"x": 300, "y": 60, "w": 75, "h": 75}, + {"x": 500, "y": 55, "w": 85, "h": 85} + ] + } +} +``` + +### No Faces Detected + +If no faces are found, the result includes a warning: + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "facesDetected": 0, + "warning": "No faces detected in this image. Try increasing detection sensitivity." + } +} +``` + +## Notes + +- Requires the `face-detection` model bundle to be installed (200-300 MB). +- Output format matches the input format automatically. +- The `faces` array contains bounding box coordinates (x, y, width, height) for each detected face. +- Increase `sensitivity` (closer to 1.0) to detect more faces, including partially occluded ones. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/border.md b/apps/docs/tools/border.md new file mode 100644 index 00000000..5ac7d77a --- /dev/null +++ b/apps/docs/tools/border.md @@ -0,0 +1,51 @@ +# Border & Frame + +Add borders, padding, rounded corners, and drop shadows to images. The tool applies effects in order: padding, border, corner radius, then shadow. + +## API Endpoint + +`POST /api/v1/tools/border` + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| borderWidth | number | No | 10 | Border thickness in pixels (0 to 2000) | +| borderColor | string | No | `"#000000"` | Border color as hex (e.g. `#FF0000`) | +| padding | number | No | 0 | Inner padding between image and border in pixels (0 to 200) | +| paddingColor | string | No | `"#FFFFFF"` | Padding fill color as hex | +| cornerRadius | number | No | 0 | Corner radius in pixels (0 to 2000) | +| shadow | boolean | No | `false` | Whether to add a drop shadow | +| shadowBlur | number | No | 15 | Shadow blur radius (1 to 200) | +| shadowOffsetX | number | No | 0 | Shadow horizontal offset (-50 to 50) | +| shadowOffsetY | number | No | 5 | Shadow vertical offset (-50 to 50) | +| shadowColor | string | No | `"#000000"` | Shadow color as hex | +| shadowOpacity | number | No | 40 | Shadow opacity percentage (0 to 100) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/border \ + -F "file=@photo.jpg" \ + -F 'settings={"borderWidth":20,"borderColor":"#333333","cornerRadius":16,"shadow":true,"shadowBlur":25,"shadowOpacity":50}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.png", + "originalSize": 456789, + "processedSize": 523456 +} +``` + +## Notes + +- Uses the standard `createToolRoute` factory. Accepts a single image file via multipart upload. +- Supports HEIC, RAW, PSD, and SVG input formats (automatically decoded). +- Processing order: padding is added first, then the border wraps around, then corner radius is applied, then the shadow is composited. +- When `cornerRadius` or `shadow` is enabled, the output is forced to PNG (regardless of input format) to preserve transparency. Formats that support alpha (PNG, WebP, AVIF) keep their original format. +- The shadow is shape-aware: it follows the rounded corners rather than creating a rectangular shadow. +- Setting `borderWidth` to 0 and using only `cornerRadius` + `shadow` creates a frameless rounded shadow effect. diff --git a/apps/docs/tools/bulk-rename.md b/apps/docs/tools/bulk-rename.md new file mode 100644 index 00000000..09917bfa --- /dev/null +++ b/apps/docs/tools/bulk-rename.md @@ -0,0 +1,72 @@ +--- +description: Rename multiple files using a pattern template and download as ZIP. +--- + +# Bulk Rename + +Rename multiple files using a pattern template with placeholders for index, padded index, and original filename. Returns a ZIP archive containing all renamed files. + +## API Endpoint + +`POST /api/v1/tools/bulk-rename` + +Accepts multipart form data with multiple files and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| pattern | string | No | `"image-{{index}}"` | Naming pattern with placeholders (max 1000 characters) | +| startIndex | number | No | `1` | Starting index number | + +### Pattern Placeholders + +| Placeholder | Description | Example | +|-------------|-------------|---------| +| `{{index}}` | Sequential number starting from `startIndex` | `1`, `2`, `3` | +| `{{padded}}` | Zero-padded sequential number | `01`, `02`, `03` | +| `{{original}}` | Original filename without extension | `photo`, `IMG_001` | + +The original file extension is always preserved. + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/bulk-rename \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo1.jpg" \ + -F "file=@photo2.jpg" \ + -F "file=@photo3.jpg" \ + -F 'settings={"pattern": "vacation-{{padded}}", "startIndex": 1}' +``` + +This produces: `vacation-1.jpg`, `vacation-2.jpg`, `vacation-3.jpg` + +Using original filename: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/bulk-rename \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@IMG_001.jpg" \ + -F "file=@IMG_002.jpg" \ + -F 'settings={"pattern": "2024-trip-{{original}}-{{index}}"}' +``` + +This produces: `2024-trip-IMG_001-1.jpg`, `2024-trip-IMG_002-2.jpg` + +## Example Response + +The response is a ZIP file streamed directly (not a JSON response). The response headers are: + +``` +Content-Type: application/zip +Content-Disposition: attachment; filename="renamed-a1b2c3d4.zip" +``` + +## Notes + +- This tool does not process images. It only renames files and packages them into a ZIP archive. +- The zero-padding width for `{{padded}}` is determined automatically based on the total number of files (e.g. 100 files would use 3-digit padding: `001`, `002`, etc.). +- File extensions are preserved from the original filenames. +- Filenames are sanitized to remove unsafe characters. +- At least one file must be provided. diff --git a/apps/docs/tools/collage.md b/apps/docs/tools/collage.md new file mode 100644 index 00000000..8dda067e --- /dev/null +++ b/apps/docs/tools/collage.md @@ -0,0 +1,85 @@ +# Collage / Grid + +Combine multiple images into beautiful grid collages with 25+ templates. Supports 2-9 image layouts with customizable gap, corner radius, background color, and per-cell pan/zoom controls. + +## API Endpoint + +`POST /api/v1/tools/collage` + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| templateId | string | Yes | - | Template layout ID (e.g. `2-h-equal`, `3-left-large`, `4-grid`, `9-grid`) | +| cells | array | No | - | Per-cell settings array with `imageIndex`, `panX`, `panY`, `zoom`, `objectFit` | +| cells[].imageIndex | integer | Yes | - | Index of the image to place in this cell (0-based) | +| cells[].panX | number | No | 0 | Horizontal pan offset (-100 to 100) | +| cells[].panY | number | No | 0 | Vertical pan offset (-100 to 100) | +| cells[].zoom | number | No | 1 | Zoom level (1 to 10) | +| cells[].objectFit | string | No | `"cover"` | How image fills cell: `cover` or `contain` | +| gap | number | No | 8 | Gap between cells in pixels (0 to 500) | +| cornerRadius | number | No | 0 | Corner radius for each cell in pixels (0 to 500) | +| backgroundColor | string | No | `"#FFFFFF"` | Background color as hex or `"transparent"` | +| aspectRatio | string | No | `"free"` | Canvas aspect ratio: `free`, `1:1`, `4:3`, `3:2`, `16:9`, `9:16`, `4:5` | +| outputFormat | string | No | `"png"` | Output format: `png`, `jpeg`, `webp`, `avif`, `jxl` | +| quality | number | No | 90 | Output quality (1 to 100) | + +## Available Templates + +| Template ID | Images | Layout | +|-------------|--------|--------| +| `2-h-equal` | 2 | Two equal columns | +| `2-v-equal` | 2 | Two equal rows | +| `2-h-left-large` | 2 | Left 2/3, right 1/3 | +| `2-h-right-large` | 2 | Left 1/3, right 2/3 | +| `3-left-large` | 3 | Large left, two stacked right | +| `3-right-large` | 3 | Two stacked left, large right | +| `3-top-large` | 3 | Large top, two columns bottom | +| `3-h-equal` | 3 | Three equal columns | +| `3-v-equal` | 3 | Three equal rows | +| `4-grid` | 4 | 2x2 grid | +| `4-left-large` | 4 | Large left, three stacked right | +| `4-top-large` | 4 | Large top, three columns bottom | +| `4-bottom-large` | 4 | Three columns top, large bottom | +| `5-top2-bottom3` | 5 | Two top, three bottom | +| `5-top3-bottom2` | 5 | Three top, two bottom | +| `5-left-large` | 5 | Large left, four stacked right | +| `5-center-large` | 5 | Large center, four corners | +| `6-grid-2x3` | 6 | 2 columns x 3 rows | +| `6-grid-3x2` | 6 | 3 columns x 2 rows | +| `6-top-large` | 6 | Large top, five columns bottom | +| `7-mosaic` | 7 | Mosaic layout | +| `8-mosaic` | 8 | Mosaic layout | +| `9-grid` | 9 | 3x3 grid | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/collage \ + -F "file=@photo1.jpg" \ + -F "file=@photo2.jpg" \ + -F "file=@photo3.jpg" \ + -F "file=@photo4.jpg" \ + -F 'settings={"templateId":"4-grid","gap":12,"cornerRadius":8,"backgroundColor":"#F5F5F5","outputFormat":"png","quality":90}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/collage.png", + "originalSize": 2456789, + "processedSize": 1823456 +} +``` + +## Notes + +- Upload multiple image files in the multipart request. The images are assigned to template cells in upload order. +- If more images are uploaded than the template supports, extra images are ignored. +- Supports HEIC, RAW, PSD, and SVG input formats (automatically decoded). +- The canvas base size is 2400px on the longest side, scaled by the chosen aspect ratio. +- When `aspectRatio` is `"free"`, the canvas defaults to 4:3 (2400x1800). +- Per-cell `panX`/`panY` values shift the crop window within the cell. A value of 100 moves fully to one edge, -100 to the other. +- The `"transparent"` background color is only preserved with `png`, `webp`, or `avif` output formats. diff --git a/apps/docs/tools/color-blindness.md b/apps/docs/tools/color-blindness.md new file mode 100644 index 00000000..9483203d --- /dev/null +++ b/apps/docs/tools/color-blindness.md @@ -0,0 +1,59 @@ +--- +description: Simulate how images appear to people with different types of color vision deficiency. +--- + +# Color Blindness Simulation + +Simulate color vision deficiency (CVD) to preview how images appear to people with various types of color blindness. Useful for accessibility testing of designs, charts, and UI. + +## API Endpoint + +`POST /api/v1/tools/color-blindness` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| simulationType | string | No | `"deuteranomaly"` | Type of color vision deficiency to simulate | + +### Simulation Types + +| Value | Condition | Description | +|-------|-----------|-------------| +| `protanopia` | Red-blind | Complete absence of red cone cells | +| `deuteranopia` | Green-blind | Complete absence of green cone cells | +| `tritanopia` | Blue-blind | Complete absence of blue cone cells | +| `protanomaly` | Red-weak | Reduced red cone sensitivity | +| `deuteranomaly` | Green-weak | Reduced green cone sensitivity (most common) | +| `tritanomaly` | Blue-weak | Reduced blue cone sensitivity | +| `achromatopsia` | Total color blind | Complete absence of color vision | +| `blueConeMonochromacy` | Blue-cone only | Only blue cones functional | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/color-blindness \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@design.png" \ + -F 'settings={"simulationType": "deuteranopia"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/design.png", + "originalSize": 1850000, + "processedSize": 1820000 +} +``` + +## Notes + +- Deuteranomaly (green-weak) is the default because it is the most common form of color vision deficiency, affecting approximately 6% of males. +- The simulation uses color transformation matrices that model how reduced or absent cone photoreceptors alter perceived colors. +- This tool is non-destructive and produces a preview only. It does not modify the original image for accessibility. +- Output format matches the input format. HEIC, RAW, PSD, and SVG inputs are automatically decoded before processing. diff --git a/apps/docs/tools/color-palette.md b/apps/docs/tools/color-palette.md new file mode 100644 index 00000000..88680e72 --- /dev/null +++ b/apps/docs/tools/color-palette.md @@ -0,0 +1,63 @@ +--- +description: Extract dominant colors from an image as a color palette. +--- + +# Color Palette + +Extract the dominant colors from an image and return them as hex color values. Uses quantized frequency analysis to identify the most prominent and visually distinct colors. + +## API Endpoint + +`POST /api/v1/tools/color-palette` + +Accepts multipart form data with an image file. No settings field is needed. + +## Parameters + +This tool has no configurable parameters. Simply upload the image file. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| file | file | Yes | The image to extract colors from | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/color-palette \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" +``` + +## Example Response + +```json +{ + "filename": "photo.jpg", + "colors": [ + "#304080", + "#e0a060", + "#f0f0f0", + "#203020", + "#a0c0e0", + "#806040" + ], + "count": 6 +} +``` + +## Response Fields + +| Field | Type | Description | +|-------|------|-------------| +| filename | string | Sanitized filename | +| colors | array | Array of hex color strings, ordered by dominance (most frequent first) | +| count | number | Number of colors extracted | + +## Notes + +- Returns up to 8 dominant colors, sorted by frequency (most common first). +- The image is internally resized to 50x50 pixels for analysis, so the palette represents overall color distribution rather than small details. +- Colors are quantized to the nearest multiple of 16 to reduce noise, then similar colors (within RGB Manhattan distance of 48) are merged to avoid near-duplicate entries. +- The alpha channel is removed before analysis, so transparent areas are not considered. +- This is a read-only endpoint. It does not produce a downloadable output file or a `jobId`. +- HEIC, RAW, PSD, and SVG inputs are automatically decoded before analysis. diff --git a/apps/docs/tools/colorize.md b/apps/docs/tools/colorize.md new file mode 100644 index 00000000..87ddcdcc --- /dev/null +++ b/apps/docs/tools/colorize.md @@ -0,0 +1,73 @@ +# AI Colorization + +Convert black-and-white or grayscale photos to full color using AI (DDColor model with OpenCV DNN fallback). + +## API Endpoint + +`POST /api/v1/tools/colorize` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `object-eraser-colorize` (1-2 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| intensity | number | No | `1.0` | Color intensity (0-1). Lower values produce more subtle colorization | +| model | string | No | `"auto"` | Model to use: `auto`, `ddcolor`, `opencv` | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/colorize \ + -F "file=@old-bw-photo.jpg" \ + -F 'settings={"intensity":0.9,"model":"auto"}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Colorizing...","percent":55} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/old-bw-photo_colorized.jpg", + "previewUrl": "/api/v1/download/{jobId}/preview.webp", + "originalSize": 180000, + "processedSize": 210000, + "width": 1920, + "height": 1080, + "method": "ddcolor" + } +} +``` + +## Notes + +- Requires the `object-eraser-colorize` model bundle to be installed (1-2 GB). +- DDColor produces higher quality results but is slower; OpenCV DNN is faster with slightly lower quality. `auto` uses DDColor when available with OpenCV fallback. +- The `intensity` parameter blends between the original grayscale and the AI-colorized result. Use 1.0 for full color, lower values for a partially desaturated vintage look. +- Output format matches the input format automatically. +- For non-browser-previewable output formats, a WebP preview is generated alongside the main output. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/compare.md b/apps/docs/tools/compare.md new file mode 100644 index 00000000..8aae29cb --- /dev/null +++ b/apps/docs/tools/compare.md @@ -0,0 +1,65 @@ +--- +description: Compare two images side by side with pixel-level diff visualization and similarity score. +--- + +# Image Compare + +Upload two images to compute a pixel-level difference map and a numerical similarity percentage. The output is a diff image highlighting changed regions in red. + +## API Endpoint + +`POST /api/v1/tools/compare` + +Accepts multipart form data with **two** image files. No settings field is needed. + +## Parameters + +This tool has no configurable parameters. Upload exactly two image files. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| file (first) | file | Yes | The first image | +| file (second) | file | Yes | The second image | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/compare \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@original.jpg" \ + -F "file=@modified.jpg" +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "similarity": 94.52, + "dimensions": { "width": 1920, "height": 1080 }, + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/diff.png", + "originalSize": 4900000, + "processedSize": 280000 +} +``` + +## Response Fields + +| Field | Type | Description | +|-------|------|-------------| +| jobId | string | Job identifier for downloading the diff image | +| similarity | number | Percentage similarity between the two images (0 to 100) | +| dimensions | object | Width and height used for comparison | +| downloadUrl | string | URL to download the generated diff image | +| originalSize | number | Combined size of both input images in bytes | +| processedSize | number | Size of the diff output image in bytes | + +## Notes + +- Both images are resized to the same dimensions (the maximum of each axis) before comparison. +- The diff image highlights differences in red with opacity proportional to the magnitude of change. Identical or near-identical pixels (difference < 10) are shown as semi-transparent versions of the original. +- Similarity is calculated as the inverse of the average pixel difference across all pixels, expressed as a percentage. +- A similarity of 100% means the images are pixel-identical (at the comparison resolution). +- The diff output is always PNG format regardless of input formats. +- Both images are validated and decoded (HEIC, RAW, PSD, SVG supported) before comparison. +- EXIF orientation is auto-applied on both images before processing. diff --git a/apps/docs/tools/compose.md b/apps/docs/tools/compose.md new file mode 100644 index 00000000..fb29d837 --- /dev/null +++ b/apps/docs/tools/compose.md @@ -0,0 +1,84 @@ +--- +description: Layer images with position, opacity, and blend modes for compositing. +--- + +# Image Composition + +Layer an overlay image on top of a base image with configurable position, opacity, and blend mode. Useful for compositing logos, graphics, or combining multiple images. + +## API Endpoint + +`POST /api/v1/tools/compose` + +Accepts multipart form data with **two** image files and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| x | number | No | `0` | Horizontal offset of the overlay from the top-left corner in pixels (min 0) | +| y | number | No | `0` | Vertical offset of the overlay from the top-left corner in pixels (min 0) | +| opacity | number | No | `100` | Overlay opacity percentage (0 to 100) | +| blendMode | string | No | `"over"` | Compositing blend mode | + +### Blend Modes + +| Value | Description | +|-------|-------------| +| `over` | Normal overlay (default) | +| `multiply` | Darken by multiplying pixel values | +| `screen` | Lighten by inverting, multiplying, and inverting again | +| `overlay` | Combines multiply and screen based on base brightness | +| `darken` | Keep the darker pixel from each layer | +| `lighten` | Keep the lighter pixel from each layer | +| `hard-light` | Strong contrast overlay | +| `soft-light` | Subtle contrast overlay | +| `difference` | Absolute difference between layers | +| `exclusion` | Similar to difference but lower contrast | + +### File Fields + +| Field Name | Required | Description | +|------------|----------|-------------| +| file | Yes | The base/background image | +| overlay | Yes | The overlay/foreground image | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/compose \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@background.jpg" \ + -F "overlay=@graphic.png" \ + -F 'settings={"x": 100, "y": 50, "opacity": 80, "blendMode": "over"}' +``` + +Using multiply blend mode: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/compose \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F "overlay=@texture.jpg" \ + -F 'settings={"x": 0, "y": 0, "opacity": 50, "blendMode": "multiply"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/background.jpg", + "originalSize": 3200000, + "processedSize": 3450000 +} +``` + +## Notes + +- Both images are validated and decoded (HEIC, RAW, PSD, SVG supported) before compositing. +- The overlay is placed at the exact pixel coordinates specified by `x` and `y`. It is not resized to fit. +- If opacity is less than 100, an alpha mask is applied to the overlay before blending. +- The overlay can extend beyond the base image boundaries (it will be clipped). +- EXIF orientation is auto-applied on both images before processing. +- Output dimensions match the base image dimensions. diff --git a/apps/docs/tools/compress.md b/apps/docs/tools/compress.md new file mode 100644 index 00000000..3fe23498 --- /dev/null +++ b/apps/docs/tools/compress.md @@ -0,0 +1,59 @@ +--- +description: Reduce image file size by quality level or to a target file size. +--- + +# Compress + +Reduce image file size by specifying a quality level or a target file size in kilobytes. The tool uses iterative binary search to hit size targets accurately. + +## API Endpoint + +`POST /api/v1/tools/compress` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| mode | string | No | `"quality"` | Compression mode: `quality` or `targetSize` | +| quality | number | No | `80` | Quality level (1-100). Used when mode is `quality`. | +| targetSizeKb | number | No | - | Target file size in kilobytes. Used when mode is `targetSize`. | + +## Example Request + +Compress to quality 60: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/compress \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"mode": "quality", "quality": 60}' +``` + +Compress to target size of 200 KB: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/compress \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"mode": "targetSize", "targetSizeKb": 200}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 204800 +} +``` + +## Notes + +- In `quality` mode, lower values produce smaller files with more compression artifacts. A value of 80 is a good default for web use. +- In `targetSize` mode, the engine performs iterative compression to get as close to the target as possible without exceeding it. +- Output format matches the input format. The compression applies to the format's native encoding (e.g. JPEG quality for JPEG files, WebP quality for WebP files). +- If the default quality (80) is acceptable, you can omit the `quality` parameter entirely. diff --git a/apps/docs/tools/content-aware-resize.md b/apps/docs/tools/content-aware-resize.md new file mode 100644 index 00000000..401ac506 --- /dev/null +++ b/apps/docs/tools/content-aware-resize.md @@ -0,0 +1,57 @@ +# Content-Aware Resize + +Seam carving resize that intelligently removes or adds pixels along paths of least visual importance, preserving important content and optionally protecting faces. + +## API Endpoint + +`POST /api/v1/tools/content-aware-resize` + +**Processing:** Synchronous (returns result directly) + +**Model bundle:** None required for basic operation. Face protection uses the `face-detection` bundle (200-300 MB) if enabled. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| width | integer | No | - | Target width in pixels | +| height | integer | No | - | Target height in pixels | +| protectFaces | boolean | No | `false` | Detect and protect faces from seam removal | +| blurRadius | number | No | `4` | Pre-processing blur radius for energy calculation (0-20) | +| sobelThreshold | number | No | `2` | Sobel edge detection threshold (1-20). Higher values make the algorithm more aggressive | +| square | boolean | No | `false` | Resize to a square (uses the smaller dimension) | + +At least one of `width`, `height`, or `square` must be specified. + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/content-aware-resize \ + -F "file=@landscape.jpg" \ + -F 'settings={"width":800,"protectFaces":true}' +``` + +## Response (200 OK) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/landscape_seam.png", + "originalSize": 450000, + "processedSize": 380000, + "width": 800, + "height": 600 +} +``` + +## Notes + +- This tool returns a synchronous response (not 202 async). Processing happens inline. +- Uses the `caire` seam carving library for content-aware resizing. +- Only reduces dimensions (removes seams). Cannot expand an image beyond its original size. +- The `protectFaces` option uses AI face detection to mark face regions as high-energy, preventing seams from passing through faces. +- `blurRadius` controls smoothing before energy map calculation. Higher values make the energy map more uniform, which can help with noisy images. +- `sobelThreshold` affects how aggressively edges are detected. Lower values preserve more subtle edges. +- Output is always PNG format. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/convert.md b/apps/docs/tools/convert.md new file mode 100644 index 00000000..aacbfe98 --- /dev/null +++ b/apps/docs/tools/convert.md @@ -0,0 +1,78 @@ +--- +description: Convert images between formats including modern formats like AVIF, JXL, and HEIC. +--- + +# Convert + +Convert images between formats. Supports common web formats as well as specialized formats like HEIC, JXL, BMP, ICO, JP2, QOI, and PSD. + +## API Endpoint + +`POST /api/v1/tools/convert` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| format | string | Yes | - | Target format: `jpg`, `png`, `webp`, `avif`, `tiff`, `gif`, `heic`, `heif`, `jxl`, `bmp`, `ico`, `jp2`, `qoi`, `psd` | +| quality | number | No | - | Output quality (1-100). Applies to lossy formats like jpg, webp, avif, heic. | + +## Supported Output Formats + +| Format | Type | Notes | +|--------|------|-------| +| jpg | Lossy | JPEG, best compatibility | +| png | Lossless | Supports transparency | +| webp | Both | Modern web format, good compression | +| avif | Lossy | Next-gen format, excellent compression | +| tiff | Both | Print/publishing workflows | +| gif | Lossless | Limited to 256 colors | +| heic / heif | Lossy | Apple ecosystem format | +| jxl | Both | JPEG XL, next-gen format | +| bmp | Lossless | Uncompressed bitmap | +| ico | Lossless | Windows icon format | +| jp2 | Lossy | JPEG 2000 | +| qoi | Lossless | Quite OK Image format | +| psd | Layered | Adobe Photoshop (requires ImageMagick) | + +## Example Request + +Convert to WebP: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/convert \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"format": "webp", "quality": 85}' +``` + +Convert to PNG (lossless): + +```bash +curl -X POST http://localhost:1349/api/v1/tools/convert \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"format": "png"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.webp", + "originalSize": 2450000, + "processedSize": 680000 +} +``` + +## Notes + +- The output filename extension is automatically updated to match the target format. +- SVG inputs are rasterized at 300 DPI before conversion. +- PSD conversion requires ImageMagick to be installed on the server. +- BMP, ICO, JP2, JXL, and QOI use specialized CLI encoders and bypass Sharp processing. +- HEIC/HEIF encoding uses the system HEIC encoder library. +- Input formats are broad: JPEG, PNG, WebP, AVIF, TIFF, GIF, HEIC, RAW (CR2, NEF, ARW, etc.), PSD, SVG, BMP, and more. diff --git a/apps/docs/tools/crop.md b/apps/docs/tools/crop.md new file mode 100644 index 00000000..438dd84d --- /dev/null +++ b/apps/docs/tools/crop.md @@ -0,0 +1,59 @@ +--- +description: Crop images by specifying a region with position and dimensions. +--- + +# Crop + +Crop images by defining a rectangular region using position and size. Supports both pixel and percentage units. + +## API Endpoint + +`POST /api/v1/tools/crop` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| left | number | Yes | - | X offset of the crop region (from left edge) | +| top | number | Yes | - | Y offset of the crop region (from top edge) | +| width | number | Yes | - | Width of the crop region | +| height | number | Yes | - | Height of the crop region | +| unit | string | No | `"px"` | Unit for the values: `px` or `percent` | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/crop \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"left": 100, "top": 50, "width": 800, "height": 600}' +``` + +Crop using percentage values: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/crop \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"left": 10, "top": 10, "width": 80, "height": 80, "unit": "percent"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 1200000 +} +``` + +## Notes + +- The crop region must fit within the image boundaries. If the region extends beyond the image, the request will fail. +- When using `percent` unit, values represent percentages of the image dimensions (e.g. `left: 10` means 10% from the left edge). +- Output format matches the input format. +- EXIF orientation is auto-applied before cropping, so coordinates correspond to the visually correct orientation. diff --git a/apps/docs/tools/edit-metadata.md b/apps/docs/tools/edit-metadata.md new file mode 100644 index 00000000..443e5fbd --- /dev/null +++ b/apps/docs/tools/edit-metadata.md @@ -0,0 +1,105 @@ +--- +description: Edit EXIF, IPTC, GPS, and XMP metadata fields in images without re-encoding pixels. +--- + +# Edit Metadata + +Edit image metadata fields including EXIF, IPTC, GPS coordinates, dates, and keywords. Uses ExifTool under the hood, so metadata is written in-place without re-encoding pixels, preserving full image quality. + +## API Endpoints + +### Edit Metadata + +`POST /api/v1/tools/edit-metadata` + +Writes metadata fields to the image and returns the modified file. + +### Inspect Metadata + +`POST /api/v1/tools/edit-metadata/inspect` + +Returns the full metadata from the image via ExifTool as JSON. Does not modify the image. + +## Parameters (Edit) + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| title | string | No | - | Image title (XMP/EXIF) | +| author | string | No | - | Author name | +| artist | string | No | - | Artist name (EXIF Artist tag) | +| copyright | string | No | - | Copyright notice | +| imageDescription | string | No | - | Image description (EXIF) | +| software | string | No | - | Software tag | +| dateTime | string | No | - | EXIF DateTime value | +| dateTimeOriginal | string | No | - | EXIF DateTimeOriginal value | +| setAllDates | string | No | - | Set all date fields at once | +| dateShift | string | No | - | Shift all dates by offset (format: `+HH:MM` or `-HH:MM`) | +| clearGps | boolean | No | `false` | Remove all GPS data | +| gpsLatitude | number | No | - | Set GPS latitude (-90 to 90) | +| gpsLongitude | number | No | - | Set GPS longitude (-180 to 180) | +| gpsAltitude | number | No | - | Set GPS altitude in meters | +| keywords | string[] | No | - | Keywords/tags to add or set | +| keywordsMode | string | No | `"add"` | How to handle keywords: `add` (append) or `set` (replace) | +| fieldsToRemove | string[] | No | `[]` | List of specific metadata field names to remove | +| iptcTitle | string | No | - | IPTC Object Name | +| iptcHeadline | string | No | - | IPTC Headline | +| iptcCity | string | No | - | IPTC City | +| iptcState | string | No | - | IPTC Province/State | +| iptcCountry | string | No | - | IPTC Country | + +## Example Request + +Set author and copyright: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/edit-metadata \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"author": "Jane Smith", "copyright": "2024 Jane Smith"}' +``` + +Set GPS coordinates: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/edit-metadata \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"gpsLatitude": 48.8566, "gpsLongitude": 2.3522, "gpsAltitude": 35}' +``` + +Remove GPS and add keywords: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/edit-metadata \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"clearGps": true, "keywords": ["landscape", "sunset"], "keywordsMode": "add"}' +``` + +Inspect metadata: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/edit-metadata/inspect \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" +``` + +## Example Response (Edit) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 2452000 +} +``` + +## Notes + +- This tool requires ExifTool to be installed on the server. It is included in the Docker image. +- Metadata is written in-place, so no pixel re-encoding occurs. The file size change is minimal (just the metadata bytes). +- The `dateShift` parameter shifts all date fields by the specified offset, useful for correcting timezone errors (e.g. `+02:00` or `-05:30`). +- If no changes are requested (all parameters omitted or empty), the original file is returned unchanged. +- Supported formats: JPEG, PNG, WebP, AVIF, TIFF, GIF, HEIC/HEIF. +- For non-browser-previewable formats (HEIF, TIFF), the response includes a `previewUrl` field with a WebP preview. diff --git a/apps/docs/tools/enhance-faces.md b/apps/docs/tools/enhance-faces.md new file mode 100644 index 00000000..eaff2c35 --- /dev/null +++ b/apps/docs/tools/enhance-faces.md @@ -0,0 +1,78 @@ +# Face Enhancement + +Restore and enhance faces in images using AI models (GFPGAN/CodeFormer). + +## API Endpoint + +`POST /api/v1/tools/enhance-faces` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `upscale-enhance` (4-5 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| model | string | No | `"auto"` | Model to use: `auto`, `gfpgan`, `codeformer` | +| strength | number | No | `0.8` | Enhancement strength (0-1). Higher values produce stronger enhancement | +| onlyCenterFace | boolean | No | `false` | Only enhance the most central/prominent face | +| sensitivity | number | No | `0.5` | Face detection sensitivity (0-1) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/enhance-faces \ + -F "file=@portrait.jpg" \ + -F 'settings={"model":"codeformer","strength":0.7,"onlyCenterFace":false}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Enhancing faces...","percent":60} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/portrait_enhanced.png", + "previewUrl": "/api/v1/download/{jobId}/preview.webp", + "originalSize": 350000, + "processedSize": 600000, + "facesDetected": 2, + "faces": [ + {"x": 120, "y": 80, "w": 100, "h": 100}, + {"x": 350, "y": 90, "w": 95, "h": 95} + ], + "model": "codeformer" + } +} +``` + +## Notes + +- Requires the `upscale-enhance` model bundle to be installed (4-5 GB). +- GFPGAN produces more aggressive enhancement; CodeFormer better preserves identity. `auto` selects the best model for the input. +- Output is always PNG format for maximum quality. +- A WebP preview is generated alongside the full-resolution output for faster frontend display. +- The `strength` parameter blends the enhanced face with the original. Use lower values (0.3-0.5) for subtle improvements, higher values (0.7-1.0) for stronger restoration. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/erase-object.md b/apps/docs/tools/erase-object.md new file mode 100644 index 00000000..2a0d0659 --- /dev/null +++ b/apps/docs/tools/erase-object.md @@ -0,0 +1,72 @@ +# Object Eraser + +Remove unwanted objects from images using AI inpainting (LaMa model). Accepts an image and a mask indicating the region to erase. + +## API Endpoint + +`POST /api/v1/tools/erase-object` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `object-eraser-colorize` (1-2 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Source image file (multipart) | +| mask | file | Yes | - | Mask image (white = area to erase, black = keep). Must be uploaded with fieldname `mask` | +| format | string | No | `"auto"` | Output format: `auto`, `png`, `jpg`, `jpeg`, `webp`, `tiff`, `gif`, `avif`, `heic`, `heif`, `jxl` | +| quality | integer | No | `95` | Output quality (1-100) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/erase-object \ + -F "file=@photo.jpg" \ + -F "mask=@mask.png" \ + -F "format=png" \ + -F "quality=95" +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Inpainting...","percent":70} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/photo_erased.png", + "previewUrl": "/api/v1/download/{jobId}/preview.webp", + "originalSize": 245000, + "processedSize": 230000 + } +} +``` + +## Notes + +- Requires the `object-eraser-colorize` model bundle to be installed (1-2 GB). +- The mask must be the same dimensions as the source image. White pixels indicate areas to erase; the AI fills them with plausible content. +- Uses LaMa (Large Mask Inpainting) for high-quality object removal. +- For non-browser-previewable output formats, a WebP preview is generated alongside the main output. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/favicon.md b/apps/docs/tools/favicon.md new file mode 100644 index 00000000..12dc8c73 --- /dev/null +++ b/apps/docs/tools/favicon.md @@ -0,0 +1,83 @@ +--- +description: Generate all standard favicon and app icon sizes from a source image. +--- + +# Favicon Generator + +Generate a complete set of favicon and app icon files from a source image. Produces all standard sizes needed for browsers, Apple devices, and Android, along with a web manifest and an HTML snippet. + +## API Endpoint + +`POST /api/v1/tools/favicon` + +Accepts multipart form data with one or more image files. No settings are required. + +## Parameters + +This tool has no configurable parameters. It generates all standard sizes automatically. + +## Generated Files + +For each input image, the following files are produced: + +| File | Size | Purpose | +|------|------|---------| +| `favicon-16x16.png` | 16x16 | Browser tab icon | +| `favicon-32x32.png` | 32x32 | Browser tab icon (HiDPI) | +| `favicon-48x48.png` | 48x48 | Desktop shortcut | +| `apple-touch-icon.png` | 180x180 | iOS home screen | +| `android-chrome-192x192.png` | 192x192 | Android home screen | +| `android-chrome-512x512.png` | 512x512 | Android splash screen | +| `favicon.ico` | 32x32 | Legacy ICO format | +| `manifest.json` | - | Web app manifest with icon references | +| `favicon-snippet.html` | - | Ready-to-use HTML link tags | + +## Example Request + +Single source image: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/favicon \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@logo.png" +``` + +Multiple source images (each gets its own set in a subfolder): + +```bash +curl -X POST http://localhost:1349/api/v1/tools/favicon \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@logo-light.png" \ + -F "file=@logo-dark.png" +``` + +## Example Response + +The response is a ZIP file streamed directly. The response headers are: + +``` +Content-Type: application/zip +Content-Disposition: attachment; filename="favicons-a1b2c3d4.zip" +``` + +## HTML Snippet Included + +The ZIP includes a `favicon-snippet.html` file you can paste into your HTML ``: + +```html + + + + + + +``` + +## Notes + +- Source images are resized using `cover` fit mode, meaning they are cropped to fill each square size. For best results, use a square source image. +- When multiple files are uploaded, each gets its own subfolder in the ZIP (named after the source file). +- For a single file upload, all outputs are at the root of the ZIP with no subfolder. +- Files that fail validation or decoding are skipped, and a `skipped-files.txt` is included in the ZIP explaining the issues. +- Supported input formats: JPEG, PNG, WebP, AVIF, TIFF, GIF, HEIC, SVG, RAW, PSD, and more. +- EXIF orientation is auto-applied before resizing. diff --git a/apps/docs/tools/find-duplicates.md b/apps/docs/tools/find-duplicates.md new file mode 100644 index 00000000..650d1fd7 --- /dev/null +++ b/apps/docs/tools/find-duplicates.md @@ -0,0 +1,112 @@ +--- +description: Detect duplicate and near-duplicate images using perceptual hashing. +--- + +# Find Duplicates + +Upload multiple images to detect duplicates and near-duplicates using perceptual hashing (dHash). Groups similar images together, identifies the best quality version in each group, and calculates potential space savings. + +## API Endpoint + +`POST /api/v1/tools/find-duplicates` + +Accepts multipart form data with multiple image files and an optional JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| threshold | number | No | `8` | Maximum Hamming distance to consider images as duplicates (0 to 20). Lower = stricter matching | + +### File Fields + +Upload at least 2 image files in the multipart request (all using the `file` field name or any field name for file parts). + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/find-duplicates \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo1.jpg" \ + -F "file=@photo2.jpg" \ + -F "file=@photo3.jpg" \ + -F "file=@photo4.jpg" \ + -F 'settings={"threshold": 8}' +``` + +## Example Response + +```json +{ + "totalImages": 4, + "duplicateGroups": [ + { + "groupId": 1, + "files": [ + { + "filename": "photo1.jpg", + "similarity": 100, + "width": 4032, + "height": 3024, + "fileSize": 2450000, + "format": "jpeg", + "isBest": true, + "thumbnail": "data:image/jpeg;base64,/9j/..." + }, + { + "filename": "photo2.jpg", + "similarity": 96.88, + "width": 1920, + "height": 1440, + "fileSize": 850000, + "format": "jpeg", + "isBest": false, + "thumbnail": "data:image/jpeg;base64,/9j/..." + } + ] + } + ], + "uniqueImages": 2, + "spaceSaveable": 850000, + "skippedFiles": [] +} +``` + +## Response Fields + +| Field | Type | Description | +|-------|------|-------------| +| totalImages | number | Number of images successfully analyzed | +| duplicateGroups | array | Groups of duplicate images | +| uniqueImages | number | Number of images not part of any duplicate group | +| spaceSaveable | number | Total bytes that could be saved by removing non-best duplicates | +| skippedFiles | array | Files that could not be processed (with filename and reason) | + +### Duplicate Group Object + +| Field | Type | Description | +|-------|------|-------------| +| groupId | number | Group identifier | +| files | array | Images in this duplicate group | + +### File Object (within a group) + +| Field | Type | Description | +|-------|------|-------------| +| filename | string | Original filename | +| similarity | number | Similarity percentage to the reference image (first in group) | +| width | number | Image width in pixels | +| height | number | Image height in pixels | +| fileSize | number | File size in bytes | +| format | string | Image format | +| isBest | boolean | Whether this is the highest quality version (most pixels, largest file) | +| thumbnail | string or null | Base64 JPEG thumbnail (200px wide) for preview | + +## Notes + +- Uses a 128-bit dHash (64-bit row + 64-bit column) for perceptual similarity detection. This catches duplicates even across resizes, recompression, and minor edits. +- The threshold represents maximum Hamming distance between hashes. Default of 8 catches near-duplicates while avoiding false positives. Use 0 for pixel-identical only, or 15-20 for very loose matching. +- The "best" image in each group is the one with the most pixels (width x height), with file size as a tiebreaker. +- At least 2 images are required. Files that fail validation or decoding are reported in `skippedFiles` rather than causing the entire request to fail. +- Thumbnails are 200px-wide JPEG previews encoded as data URIs. +- All common formats are supported (HEIC, RAW, PSD, SVG decoded automatically). diff --git a/apps/docs/tools/gif-tools.md b/apps/docs/tools/gif-tools.md new file mode 100644 index 00000000..1e25fd63 --- /dev/null +++ b/apps/docs/tools/gif-tools.md @@ -0,0 +1,140 @@ +# GIF Tools + +Resize, optimize, change speed, reverse, extract frames, and rotate animated GIFs. Provides multiple operation modes in a single tool. + +## API Endpoint + +`POST /api/v1/tools/gif-tools` + +## Parameters + +### Common Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| mode | string | No | `"resize"` | Operation mode: `resize`, `optimize`, `speed`, `reverse`, `extract`, `rotate` | +| loop | number | No | 0 | Loop count for output GIF (0 = infinite, 1-100 = finite loops) | + +### Resize Mode Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| width | integer | No | - | Target width in pixels (1 to 16384) | +| height | integer | No | - | Target height in pixels (1 to 16384) | +| percentage | number | No | - | Scale by percentage (1 to 500). Overrides width/height if set. | + +### Optimize Mode Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| colors | number | No | 256 | Maximum number of colors in palette (2 to 256) | +| dither | number | No | 1.0 | Dithering strength (0 to 1, where 0 disables dithering) | +| effort | number | No | 7 | Optimization effort level (1 to 10, higher = slower but smaller) | + +### Speed Mode Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| speedFactor | number | No | 1.0 | Speed multiplier (0.1 to 10). Values > 1 speed up, < 1 slow down. | + +### Extract Mode Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| extractMode | string | No | `"single"` | Extraction mode: `single`, `range`, `all` | +| frameNumber | number | No | 0 | Frame index to extract in `single` mode (0-based) | +| frameStart | number | No | 0 | Start frame index for `range` mode (0-based) | +| frameEnd | number | No | - | End frame index for `range` mode (0-based, inclusive) | +| extractFormat | string | No | `"png"` | Format for extracted frames: `png`, `webp` | + +### Rotate Mode Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| angle | number | No | - | Rotation angle: `90`, `180`, or `270` degrees | +| flipH | boolean | No | `false` | Flip horizontally | +| flipV | boolean | No | `false` | Flip vertically | + +## Example Requests + +### Resize + +```bash +curl -X POST http://localhost:13490/api/v1/tools/gif-tools \ + -F "file=@animation.gif" \ + -F 'settings={"mode":"resize","percentage":50}' +``` + +### Optimize + +```bash +curl -X POST http://localhost:13490/api/v1/tools/gif-tools \ + -F "file=@large.gif" \ + -F 'settings={"mode":"optimize","colors":128,"effort":9}' +``` + +### Speed Up + +```bash +curl -X POST http://localhost:13490/api/v1/tools/gif-tools \ + -F "file=@animation.gif" \ + -F 'settings={"mode":"speed","speedFactor":2.0}' +``` + +### Extract Single Frame + +```bash +curl -X POST http://localhost:13490/api/v1/tools/gif-tools \ + -F "file=@animation.gif" \ + -F 'settings={"mode":"extract","extractMode":"single","frameNumber":5,"extractFormat":"png"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/animation.gif", + "originalSize": 2345678, + "processedSize": 1234567 +} +``` + +## Info Sub-Route + +`POST /api/v1/tools/gif-tools/info` + +Returns metadata about an animated GIF without processing it. + +### Info Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/gif-tools/info \ + -F "file=@animation.gif" +``` + +### Info Response + +```json +{ + "width": 480, + "height": 320, + "pages": 24, + "delay": [100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100], + "loop": 0, + "fileSize": 2345678, + "duration": 2400 +} +``` + +## Notes + +- Uses the standard `createToolRoute` factory for the main processing endpoint. +- The info endpoint only requires a file upload (no settings needed). +- In `resize` mode, if `percentage` is provided it takes priority over `width`/`height`. The resize uses `fit: inside` to maintain aspect ratio. +- In `speed` mode, frame delays are divided by the speed factor. Minimum delay per frame is 20ms (GIF spec limitation). +- In `reverse` mode, the `speedFactor` parameter is also available to simultaneously adjust speed while reversing. +- In `extract` mode with `range` or `all`, the output is a ZIP file containing individual frames. +- In `rotate` mode, each frame is processed individually and reassembled into an animation. +- The `loop` parameter controls how many times the output GIF loops. Use 0 for infinite looping. +- The `duration` field in the info response is the total animation duration in milliseconds. diff --git a/apps/docs/tools/html-to-image.md b/apps/docs/tools/html-to-image.md new file mode 100644 index 00000000..d328d315 --- /dev/null +++ b/apps/docs/tools/html-to-image.md @@ -0,0 +1,78 @@ +--- +description: Capture webpages or HTML snippets as high-quality images with device emulation. +--- + +# HTML to Image + +Capture a webpage URL or raw HTML content as a screenshot image. Supports device emulation (desktop, tablet, mobile), full-page capture, and multiple output formats. + +## API Endpoint + +`POST /api/v1/tools/html-to-image` + +Accepts a **JSON body** (not multipart). No file upload is needed. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| url | string | Conditional | - | URL to capture (must be a valid URL) | +| html | string | Conditional | - | Raw HTML content to render (1 to 5,000,000 characters) | +| format | string | No | `"png"` | Output format: `jpg`, `png`, `webp` | +| quality | number | No | `90` | Output quality for lossy formats (1 to 100) | +| fullPage | boolean | No | `false` | Capture the full scrollable page, not just the viewport | +| devicePreset | string | No | `"desktop"` | Device emulation: `desktop`, `tablet`, `mobile`, `custom` | +| viewportWidth | number | No | `1280` | Custom viewport width in pixels (320 to 3840, used when devicePreset is `custom`) | +| viewportHeight | number | No | `720` | Custom viewport height in pixels (320 to 2160, used when devicePreset is `custom`) | + +Either `url` or `html` must be provided, but not both. + +### Device Presets + +| Preset | Width | Height | Mobile UA | +|--------|-------|--------|-----------| +| `desktop` | 1280 | 720 | No | +| `tablet` | 768 | 1024 | No | +| `mobile` | 375 | 812 | Yes | +| `custom` | (user-specified) | (user-specified) | No | + +## Example Request + +Capture a webpage: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/html-to-image \ + -H "Authorization: Bearer si_your-api-key" \ + -H "Content-Type: application/json" \ + -d '{"url": "https://example.com", "format": "png", "fullPage": true, "devicePreset": "desktop"}' +``` + +Render HTML content: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/html-to-image \ + -H "Authorization: Bearer si_your-api-key" \ + -H "Content-Type: application/json" \ + -d '{"html": "

Hello

", "format": "png"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/screenshot.png", + "originalSize": 0, + "processedSize": 145000 +} +``` + +## Notes + +- Requires Chromium to be installed on the server. Returns HTTP 503 if the browser service is not available. +- URLs are validated against SSRF attacks (private/internal network addresses are blocked). +- This endpoint is rate-limited to 120 requests per hour. +- `originalSize` is always 0 since this tool generates images from URLs/HTML. +- The output filename is `screenshot.`. +- If the page takes too long to load, the request returns HTTP 504 (gateway timeout). +- If the browser service crashes repeatedly, it is temporarily disabled and returns HTTP 503 with code `BROWSER_CRASHED`. diff --git a/apps/docs/tools/image-enhancement.md b/apps/docs/tools/image-enhancement.md new file mode 100644 index 00000000..d044950f --- /dev/null +++ b/apps/docs/tools/image-enhancement.md @@ -0,0 +1,92 @@ +# Image Enhancement + +One-click auto-improve with smart analysis. Analyzes the image and applies exposure, contrast, white balance, saturation, sharpness, and denoising corrections. + +## API Endpoint + +`POST /api/v1/tools/image-enhancement` + +**Processing:** Synchronous (uses `createToolRoute` factory, returns result directly) + +**Model bundle:** None required for basic enhancement. The `upscale-enhance` bundle (4-5 GB) is used only when `deepEnhance` is enabled (for AI noise removal via SCUNet). + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| mode | string | No | `"auto"` | Enhancement mode: `auto`, `portrait`, `landscape`, `low-light`, `food`, `document` | +| intensity | number | No | `50` | Overall enhancement intensity (0-100) | +| corrections | object | No | all `true` | Selective corrections to apply (see below) | +| deepEnhance | boolean | No | `false` | Enable AI-powered noise removal (requires `noise-removal` tool installed) | + +### Corrections Object + +| Field | Type | Default | Description | +|-------|------|---------|-------------| +| exposure | boolean | `true` | Auto-correct exposure | +| contrast | boolean | `true` | Auto-correct contrast | +| whiteBalance | boolean | `true` | Auto-correct white balance | +| saturation | boolean | `true` | Auto-correct saturation | +| sharpness | boolean | `true` | Auto-sharpen | +| denoise | boolean | `true` | Light denoising | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/image-enhancement \ + -F "file=@photo.jpg" \ + -F 'settings={"mode":"portrait","intensity":70,"corrections":{"exposure":true,"contrast":true,"sharpness":false}}' +``` + +## Response (200 OK) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/photo_enhanced.jpg", + "originalSize": 300000, + "processedSize": 310000 +} +``` + +## Analyze Endpoint + +`POST /api/v1/tools/image-enhancement/analyze` + +Analyzes an image and returns correction recommendations without applying them. + +### Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| file | file | Yes | Image file (multipart) | + +### Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/image-enhancement/analyze \ + -F "file=@photo.jpg" +``` + +### Response (200 OK) + +```json +{ + "corrections": { + "exposure": { "value": 0.3, "direction": "brighten" }, + "contrast": { "value": 0.2, "direction": "increase" }, + "whiteBalance": { "value": 200, "direction": "warmer" }, + "saturation": { "value": 0.1, "direction": "increase" }, + "sharpness": { "value": 0.4, "direction": "sharpen" } + } +} +``` + +## Notes + +- This tool uses the synchronous `createToolRoute` factory, so it returns a standard response (not 202 async). +- The `mode` parameter adjusts how corrections are weighted (e.g., portrait mode is gentler on skin tones, landscape mode boosts saturation). +- When `deepEnhance` is enabled and the `noise-removal` tool (SCUNet) is installed, an additional AI denoising pass is applied after the standard corrections. +- The analyze endpoint is useful for previewing what corrections would be applied before committing. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/image-to-base64.md b/apps/docs/tools/image-to-base64.md new file mode 100644 index 00000000..f358cb7e --- /dev/null +++ b/apps/docs/tools/image-to-base64.md @@ -0,0 +1,93 @@ +--- +description: Convert images to base64 data URIs for embedding in HTML, CSS, and more. +--- + +# Image to Base64 + +Convert one or more images to base64-encoded strings and data URIs. Supports optional format conversion, quality control, and resizing. Useful for embedding images directly in HTML, CSS, JSON, or email templates. + +## API Endpoint + +`POST /api/v1/tools/image-to-base64` + +Accepts multipart form data with one or more image files and an optional JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| outputFormat | string | No | `"original"` | Convert before encoding: `original`, `jpeg`, `png`, `webp`, `avif`, `jxl` | +| quality | number | No | `80` | Output quality for lossy formats (1 to 100) | +| maxWidth | number | No | `0` | Maximum width in pixels (0 = no resize, will not enlarge) | +| maxHeight | number | No | `0` | Maximum height in pixels (0 = no resize, will not enlarge) | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/image-to-base64 \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@icon.png" \ + -F 'settings={"outputFormat": "webp", "quality": 80, "maxWidth": 200}' +``` + +Multiple files: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/image-to-base64 \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@icon1.png" \ + -F "file=@icon2.png" \ + -F "file=@icon3.png" \ + -F 'settings={"outputFormat": "original"}' +``` + +## Example Response + +```json +{ + "results": [ + { + "filename": "icon.png", + "mimeType": "image/webp", + "width": 200, + "height": 200, + "originalSize": 45000, + "encodedSize": 28800, + "overheadPercent": -36.0, + "base64": "UklGRlYAAABXRUJQ...", + "dataUri": "data:image/webp;base64,UklGRlYAAABXRUJQ..." + } + ], + "errors": [] +} +``` + +## Response Fields + +| Field | Type | Description | +|-------|------|-------------| +| results | array | Successfully converted images | +| errors | array | Images that failed to process (with filename and error message) | + +### Result Object + +| Field | Type | Description | +|-------|------|-------------| +| filename | string | Original filename | +| mimeType | string | MIME type of the encoded output | +| width | number | Final width in pixels (after any resizing) | +| height | number | Final height in pixels (after any resizing) | +| originalSize | number | Original file size in bytes | +| encodedSize | number | Size of the base64 string in bytes | +| overheadPercent | number | Percentage size difference vs original (positive = larger, negative = smaller) | +| base64 | string | Raw base64-encoded image data | +| dataUri | string | Complete data URI ready for use in `src` attributes | + +## Notes + +- Base64 encoding typically increases size by approximately 33% compared to the binary file. The `overheadPercent` field shows the actual difference. +- When `outputFormat` is `"original"`, HEIC/HEIF files are converted to JPEG (since browsers cannot display HEIC in data URIs). +- The `maxWidth` and `maxHeight` options resize using `fit: inside` with `withoutEnlargement`, so images smaller than the specified dimensions are not upscaled. +- Multiple files can be processed in a single request. Each file is processed independently, and failures do not prevent other files from succeeding. +- SVG files are passed through as `image/svg+xml` without re-encoding (unless a format conversion is requested). +- This is a read-only endpoint. It does not produce a downloadable file or a `jobId`. The base64 data is returned directly in the response body. diff --git a/apps/docs/tools/image-to-pdf.md b/apps/docs/tools/image-to-pdf.md new file mode 100644 index 00000000..2c7eea98 --- /dev/null +++ b/apps/docs/tools/image-to-pdf.md @@ -0,0 +1,116 @@ +--- +description: Combine one or more images into a PDF document with page size, orientation, and target file size options. +--- + +# Image to PDF + +Combine one or more images into a PDF document. Supports multiple page sizes, orientations, margins, and optional file size targeting via quality adjustment. + +## API Endpoint + +`POST /api/v1/tools/image-to-pdf` + +Accepts multipart form data with one or more image files and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| pageSize | string | No | `"A4"` | Page size: `A4`, `Letter`, `A3`, `A5` | +| orientation | string | No | `"portrait"` | Page orientation: `portrait` or `landscape` | +| margin | number | No | `20` | Page margin in points (0-500) | +| targetSize | object | No | - | Target file size constraint (see below) | +| collate | boolean | No | `true` | Combine all images into one PDF. If `false`, creates one PDF per image. | + +### Target Size Object + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| value | number | Yes | Target size value | +| unit | string | Yes | Unit: `KB` or `MB` | + +Minimum target size is 50 KB. + +## Example Request + +Basic multi-image PDF: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/image-to-pdf \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@page1.jpg" \ + -F "file=@page2.jpg" \ + -F "file=@page3.jpg" \ + -F 'settings={"pageSize": "A4", "orientation": "portrait", "margin": 20}' +``` + +With file size target: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/image-to-pdf \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@scan1.jpg" \ + -F "file=@scan2.jpg" \ + -F 'settings={"pageSize": "Letter", "targetSize": {"value": 2, "unit": "MB"}}' +``` + +One PDF per image: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/image-to-pdf \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo1.jpg" \ + -F "file=@photo2.jpg" \ + -F 'settings={"collate": false}' +``` + +## Example Response (Collated) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/images.pdf", + "originalSize": 5000000, + "processedSize": 1200000, + "pages": 3 +} +``` + +## Example Response (Non-Collated) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/images.zip", + "originalSize": 5000000, + "processedSize": 2400000, + "pages": 2, + "collated": false +} +``` + +## Example Response (With Target Size) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/images.pdf", + "originalSize": 10000000, + "processedSize": 2000000, + "pages": 5, + "compression": { + "targetRequested": 2097152, + "targetMet": true, + "jpegQuality": 72 + } +} +``` + +## Notes + +- Images are centered on the page and scaled to fit within the margins while preserving aspect ratio. Images are never upscaled. +- When `collate` is `false`, each image becomes a separate PDF file, and the download is a ZIP archive containing all PDFs. +- The target size feature uses iterative binary search over JPEG quality levels (10-95) to find the best quality that fits within the budget. +- Transparent images are flattened to white before embedding in the PDF. +- Supported input formats: JPEG, PNG, WebP, AVIF, TIFF, GIF, HEIC, RAW, PSD, SVG, and more. +- EXIF orientation is auto-applied before embedding. diff --git a/apps/docs/tools/info.md b/apps/docs/tools/info.md new file mode 100644 index 00000000..2b2e56f3 --- /dev/null +++ b/apps/docs/tools/info.md @@ -0,0 +1,89 @@ +--- +description: View detailed image metadata, properties, and per-channel histogram statistics. +--- + +# Image Info + +Read-only analysis tool that returns comprehensive image metadata including dimensions, format, color space, EXIF/ICC/XMP presence, and per-channel histogram statistics. Does not produce a processed output file. + +## API Endpoint + +`POST /api/v1/tools/info` + +Accepts multipart form data with an image file. No settings field is needed. + +## Parameters + +This tool has no configurable parameters. Simply upload the image file. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| file | file | Yes | The image to analyze | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/info \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" +``` + +## Example Response + +```json +{ + "filename": "photo.jpg", + "fileSize": 2450000, + "width": 4032, + "height": 3024, + "format": "jpeg", + "channels": 3, + "hasAlpha": false, + "colorSpace": "srgb", + "density": 72, + "isProgressive": false, + "orientation": 1, + "hasProfile": true, + "hasExif": true, + "hasIcc": true, + "hasXmp": false, + "bitDepth": "8", + "pages": 1, + "histogram": [ + { "channel": "red", "min": 0, "max": 255, "mean": 128.45, "stdev": 52.31 }, + { "channel": "green", "min": 2, "max": 253, "mean": 115.22, "stdev": 48.76 }, + { "channel": "blue", "min": 0, "max": 250, "mean": 102.89, "stdev": 55.14 } + ] +} +``` + +## Response Fields + +| Field | Type | Description | +|-------|------|-------------| +| filename | string | Sanitized filename | +| fileSize | number | File size in bytes | +| width | number | Image width in pixels | +| height | number | Image height in pixels | +| format | string | Detected format (jpeg, png, webp, etc.) | +| channels | number | Number of color channels | +| hasAlpha | boolean | Whether the image has an alpha channel | +| colorSpace | string | Color space (srgb, cmyk, etc.) | +| density | number or null | DPI/PPI resolution | +| isProgressive | boolean | Whether JPEG uses progressive encoding | +| orientation | number or null | EXIF orientation value (1-8) | +| hasProfile | boolean | Whether an ICC profile is embedded | +| hasExif | boolean | Whether EXIF metadata is present | +| hasIcc | boolean | Whether an ICC color profile is present | +| hasXmp | boolean | Whether XMP metadata is present | +| bitDepth | string or null | Bits per sample | +| pages | number | Number of pages (for multi-page formats like TIFF, GIF) | +| histogram | array | Per-channel statistics (min, max, mean, standard deviation) | + +## Notes + +- This is a read-only endpoint. It does not produce a downloadable output file or a `jobId`. +- For RAW format images (DNG, CR2, NEF, ARW, etc.), ExifTool is used to extract true sensor dimensions and metadata flags that Sharp cannot read directly. +- HEIC/HEIF files are decoded to PNG internally to extract pixel statistics, since Sharp cannot decode HEVC pixels. +- The histogram provides min/max/mean/stdev per channel, not a full 256-bin distribution. +- The `density` field reflects the embedded DPI metadata, if present. diff --git a/apps/docs/tools/meme-generator.md b/apps/docs/tools/meme-generator.md new file mode 100644 index 00000000..38cd6fb7 --- /dev/null +++ b/apps/docs/tools/meme-generator.md @@ -0,0 +1,89 @@ +--- +description: Create memes with templates or custom images, styled text boxes, and font options. +--- + +# Meme Generator + +Create memes using built-in templates or custom images. Add text with classic meme styling (bold, outlined text), multiple layout presets, and font choices. + +## API Endpoint + +`POST /api/v1/tools/meme-generator` + +Accepts either: +- **Multipart form data** with an image file and a JSON `settings` field (custom image mode) +- **JSON body** with a `templateId` (template mode, no file upload needed) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| templateId | string | No | - | Built-in meme template ID. If provided, no image upload is needed | +| textLayout | string | No | `"top-bottom"` | Text box layout: `top-bottom`, `top-only`, `bottom-only`, `center`, `side-by-side` | +| textBoxes | array | No | `[]` | Array of text box objects with `id` and `text` fields | +| fontFamily | string | No | `"anton"` | Font: `anton`, `arial-black`, `comic-sans`, `montserrat`, `bebas-neue`, `permanent-marker`, `roboto` | +| fontSize | number | No | auto | Font size in pixels (8 to 200). Auto-calculated if omitted | +| textColor | string | No | `"#ffffff"` | Text fill color | +| strokeColor | string | No | `"#000000"` | Text stroke/outline color | +| textAlign | string | No | `"center"` | Text alignment: `left`, `center`, `right` | +| allCaps | boolean | No | `true` | Convert text to uppercase | + +### Text Boxes + +Each entry in the `textBoxes` array should have: + +| Field | Type | Description | +|-------|------|-------------| +| id | string | Box identifier matching the layout (e.g., `"top"`, `"bottom"`, `"left"`, `"right"`, `"center"`) | +| text | string | The meme text to display | + +### Text Layout Box IDs + +| Layout | Available Box IDs | +|--------|-------------------| +| `top-bottom` | `top`, `bottom` | +| `top-only` | `top` | +| `bottom-only` | `bottom` | +| `center` | `center` | +| `side-by-side` | `left`, `right` | + +## Example Request + +Custom image with top and bottom text: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/meme-generator \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"textLayout": "top-bottom", "textBoxes": [{"id": "top", "text": "When the code works"}, {"id": "bottom", "text": "On the first try"}], "fontFamily": "anton", "allCaps": true}' +``` + +Using a built-in template (JSON body, no file upload): + +```bash +curl -X POST http://localhost:1349/api/v1/tools/meme-generator \ + -H "Authorization: Bearer si_your-api-key" \ + -H "Content-Type: application/json" \ + -d '{"templateId": "drake", "textBoxes": [{"id": "top", "text": "Manual testing"}, {"id": "bottom", "text": "Automated tests"}]}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/meme-drake.png", + "originalSize": 450000, + "processedSize": 520000 +} +``` + +## Notes + +- Either `templateId` or an uploaded image file is required. Providing both uses the template. +- Templates define their own text box positions; the `textLayout` parameter is ignored when using templates. +- Text is rendered as SVG with stroke outlines for the classic meme look. +- Font size is auto-calculated to fit the text box if not explicitly set. +- Empty text boxes are skipped (no rendering occurs if all boxes are empty). +- The output filename includes the template ID when using templates (e.g., `meme-drake.png`). +- HEIC, RAW, PSD, and SVG inputs are automatically decoded before processing. diff --git a/apps/docs/tools/noise-removal.md b/apps/docs/tools/noise-removal.md new file mode 100644 index 00000000..37f1cd54 --- /dev/null +++ b/apps/docs/tools/noise-removal.md @@ -0,0 +1,72 @@ +# Noise Removal + +AI-powered noise and grain removal with multi-tier quality options, using the Python sidecar (SCUNet model). + +## API Endpoint + +`POST /api/v1/tools/noise-removal` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `upscale-enhance` (4-5 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| tier | string | No | `"balanced"` | Quality tier: `quick`, `balanced`, `quality`, `maximum` | +| strength | number | No | `50` | Denoising strength (0-100) | +| detailPreservation | number | No | `50` | How much detail to preserve (0-100). Higher values keep more texture | +| colorNoise | number | No | `30` | Color noise reduction strength (0-100) | +| format | string | No | `"original"` | Output format: `original`, `png`, `jpeg`, `webp`, `avif`, `jxl` | +| quality | number | No | `90` | Output encoding quality (1-100) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/noise-removal \ + -F "file=@noisy-photo.jpg" \ + -F 'settings={"tier":"quality","strength":60,"detailPreservation":70,"colorNoise":40}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Denoising...","percent":65} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/noisy-photo_denoised.jpg", + "originalSize": 500000, + "processedSize": 380000 + } +} +``` + +## Notes + +- Requires the `upscale-enhance` model bundle to be installed (4-5 GB). +- Quality tiers trade speed for quality: `quick` is fastest with basic denoising, `maximum` uses the most thorough multi-pass approach. +- The `detailPreservation` parameter is critical for textured subjects (fabric, hair, foliage). Higher values prevent the denoiser from smoothing away fine detail. +- When `format` is set to `"original"`, the output format matches the input file format. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/ocr.md b/apps/docs/tools/ocr.md new file mode 100644 index 00000000..2c730fa3 --- /dev/null +++ b/apps/docs/tools/ocr.md @@ -0,0 +1,58 @@ +# OCR / Text Extraction + +Extract text from images using AI-powered optical character recognition. Supports multiple languages and quality tiers. + +## API Endpoint + +`POST /api/v1/tools/ocr` + +**Processing:** Synchronous (returns extracted text directly, though progress is reported via SSE if a `clientJobId` is provided) + +**Model bundle:** `ocr` (3-4 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| quality | string | No | `"balanced"` | Quality tier: `fast` (Tesseract), `balanced` (PaddleOCR v5), `best` (PaddleOCR VL) | +| language | string | No | `"auto"` | Language hint: `auto`, `en`, `de`, `fr`, `es`, `zh`, `ja`, `ko` | +| enhance | boolean | No | `true` | Pre-process image for better OCR accuracy | +| engine | string | No | - | Deprecated. Use `quality` instead. Maps `tesseract` to `fast`, `paddleocr` to `balanced` | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/ocr \ + -F "file=@document.png" \ + -F 'settings={"quality":"best","language":"en","enhance":true}' +``` + +## Response (200 OK) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "filename": "document.png", + "text": "Extracted text content from the image...", + "engine": "paddleocr-vl" +} +``` + +### Progress (SSE, optional) + +If a `clientJobId` is provided, progress events are streamed: + +``` +event: progress +data: {"phase":"processing","stage":"Recognizing text...","percent":50} +``` + +## Notes + +- Requires the `ocr` model bundle to be installed (3-4 GB). +- Unlike most AI tools, OCR returns a synchronous JSON response with extracted text (not an image download URL). +- Uses a fallback chain: if a higher-quality tier crashes (e.g., PaddleOCR segfault), it automatically retries with the next lower tier. +- If a tier returns empty text without crashing, it also falls back to the next tier. +- Quality tiers map to engines: `fast` = Tesseract, `balanced` = PaddleOCR v5, `best` = PaddleOCR VL. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/optimize-for-web.md b/apps/docs/tools/optimize-for-web.md new file mode 100644 index 00000000..88e5160c --- /dev/null +++ b/apps/docs/tools/optimize-for-web.md @@ -0,0 +1,71 @@ +--- +description: Optimize images for web delivery with format conversion, quality control, resizing, and metadata stripping. +--- + +# Optimize for Web + +Optimize images for web delivery in a single step. Combines format conversion, quality adjustment, optional resizing, progressive encoding, and metadata stripping. + +## API Endpoint + +`POST /api/v1/tools/optimize-for-web` + +Accepts multipart form data with an image file and a JSON `settings` field. + +A live preview endpoint is also available at `POST /api/v1/tools/optimize-for-web/preview`, which returns the processed image directly as binary (no workspace creation) for real-time parameter tuning. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| format | string | No | `"webp"` | Output format: `webp`, `jpeg`, `avif`, `png`, `jxl` | +| quality | number | No | `80` | Output quality (1-100) | +| maxWidth | number | No | - | Maximum width in pixels. Image is downscaled if wider. | +| maxHeight | number | No | - | Maximum height in pixels. Image is downscaled if taller. | +| progressive | boolean | No | `true` | Enable progressive/interlaced encoding | +| stripMetadata | boolean | No | `true` | Remove EXIF, GPS, ICC, and XMP metadata | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/optimize-for-web \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"format": "webp", "quality": 75, "maxWidth": 1920}' +``` + +Optimize for AVIF with aggressive compression: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/optimize-for-web \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"format": "avif", "quality": 50, "maxWidth": 1200, "maxHeight": 800}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.webp", + "originalSize": 4500000, + "processedSize": 320000 +} +``` + +### Preview Endpoint Response + +The preview endpoint (`/api/v1/tools/optimize-for-web/preview`) returns the binary image directly with informational headers: + +- `X-Original-Size` - Original file size in bytes +- `X-Processed-Size` - Processed file size in bytes +- `X-Output-Filename` - URL-encoded output filename + +## Notes + +- This tool is designed as a one-stop optimization pipeline for web assets. It handles format conversion, quality tuning, max dimension capping, and metadata removal in a single pass. +- The output filename extension is updated to match the chosen format. +- JXL (JPEG XL) encoding uses a specialized CLI encoder. The image is first processed as PNG, then encoded to JXL. +- Progressive encoding improves perceived load time for JPEG and PNG by allowing browsers to render a low-quality preview before the full image loads. +- The preview endpoint is lighter weight (no workspace/job creation) and is intended for the frontend's live parameter tuning UI. diff --git a/apps/docs/tools/passport-photo.md b/apps/docs/tools/passport-photo.md new file mode 100644 index 00000000..b894e956 --- /dev/null +++ b/apps/docs/tools/passport-photo.md @@ -0,0 +1,166 @@ +# Passport Photo + +AI-powered passport and ID photo generator. Two-phase workflow: analyze (face detection + background removal) then generate (crop, resize, and tile for printing). + +## API Endpoints + +This tool uses a two-phase flow with separate endpoints for analysis and generation. + +**Model bundle:** `background-removal` (4-5 GB) + +--- + +### Phase 1: Analyze + +`POST /api/v1/tools/passport-photo/analyze` + +Detects face landmarks and removes the background. Returns landmark data and a preview for the frontend to display a crop preview. + +#### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| clientJobId | string | No | - | Optional job ID for progress tracking via SSE | + +#### Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/passport-photo/analyze \ + -F "file=@headshot.jpg" +``` + +#### Response (200 OK) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "filename": "headshot.jpg", + "preview": "", + "previewWidth": 800, + "previewHeight": 1067, + "landmarks": { + "leftEye": { "x": 0.42, "y": 0.35 }, + "rightEye": { "x": 0.58, "y": 0.35 }, + "eyeCenter": { "x": 0.50, "y": 0.35 }, + "chin": { "x": 0.50, "y": 0.65 }, + "forehead": { "x": 0.50, "y": 0.22 }, + "crown": { "x": 0.50, "y": 0.18 }, + "nose": { "x": 0.50, "y": 0.48 }, + "faceCenterX": 0.50 + }, + "imageWidth": 2400, + "imageHeight": 3200 +} +``` + +#### Progress (SSE, optional) + +If `clientJobId` is provided, progress is streamed (0-30% for face detection, 30-95% for background removal). + +#### Error: No Face Detected (422) + +```json +{ + "error": "No face detected", + "details": "Could not detect a face in the uploaded image. Please upload a clear, front-facing photo with good lighting." +} +``` + +--- + +### Phase 2: Generate + +`POST /api/v1/tools/passport-photo/generate` + +Crops, resizes, and optionally tiles the photo onto a print sheet. Uses cached images from Phase 1 (no AI re-run). + +#### Parameters (JSON body) + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| jobId | string | Yes | - | Job ID from Phase 1 | +| filename | string | Yes | - | Original filename from Phase 1 | +| countryCode | string | Yes | - | Country code for passport spec (e.g., `US`, `GB`, `IN`) | +| documentType | string | No | `"passport"` | Document type (from country spec) | +| bgColor | string | No | `"#FFFFFF"` | Background color hex | +| printLayout | string | No | `"none"` | Print paper layout: `none`, `4x6`, `a4`, `letter` | +| maxFileSizeKb | number | No | `0` | Max file size constraint in KB (0 = no limit) | +| dpi | number | No | `300` | Output DPI (72-1200) | +| customWidthMm | number | No | - | Custom photo width in mm (overrides country spec) | +| customHeightMm | number | No | - | Custom photo height in mm (overrides country spec) | +| zoom | number | No | `1` | Zoom factor (0.5-3). Values > 1 crop tighter | +| adjustX | number | No | `0` | Horizontal position adjustment | +| adjustY | number | No | `0` | Vertical position adjustment | +| landmarks | object | Yes | - | Landmarks object from Phase 1 response | +| imageWidth | number | Yes | - | Image width from Phase 1 response | +| imageHeight | number | Yes | - | Image height from Phase 1 response | + +#### Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/passport-photo/generate \ + -H "Content-Type: application/json" \ + -d '{ + "jobId": "a1b2c3d4-...", + "filename": "headshot.jpg", + "countryCode": "US", + "documentType": "passport", + "bgColor": "#FFFFFF", + "printLayout": "4x6", + "dpi": 300, + "zoom": 1, + "adjustX": 0, + "adjustY": 0, + "landmarks": { "leftEye": {"x":0.42,"y":0.35}, "rightEye": {"x":0.58,"y":0.35}, "eyeCenter": {"x":0.50,"y":0.35}, "chin": {"x":0.50,"y":0.65}, "forehead": {"x":0.50,"y":0.22}, "crown": {"x":0.50,"y":0.18}, "nose": {"x":0.50,"y":0.48}, "faceCenterX": 0.50 }, + "imageWidth": 2400, + "imageHeight": 3200 + }' +``` + +#### Response (200 OK) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/headshot_passport.jpg", + "dimensions": { + "widthMm": 51, + "heightMm": 51, + "widthPx": 602, + "heightPx": 602, + "dpi": 300 + }, + "spec": { + "country": "United States", + "countryCode": "US", + "documentType": "passport", + "documentLabel": "Passport" + }, + "printDownloadUrl": "/api/v1/download/{jobId}/headshot_passport_print_4x6.jpg" +} +``` + +--- + +### Base Route + +`POST /api/v1/tools/passport-photo` + +Returns guidance to use the correct sub-endpoint. + +```json +{ + "error": "Use /api/v1/tools/passport-photo/analyze or /generate" +} +``` + +## Notes + +- Requires the `background-removal` model bundle to be installed (4-5 GB). +- Phase 1 runs AI (face landmarks + background removal) and caches results. Phase 2 is pure Sharp image manipulation (fast, no AI needed). +- Landmarks are returned as normalized coordinates (0-1 range relative to image dimensions). +- The `preview` field in the analyze response is a base64-encoded PNG (max 800px wide) for fast display. +- Country specs include document dimensions, head height ratios, and eye-line positioning based on official passport photo requirements. +- The `printLayout` option generates a tiled sheet on standard paper sizes (4x6", A4, Letter) with 2mm gutters between photos. +- When `maxFileSizeKb` is set, the output is iteratively compressed to fit within the size limit. diff --git a/apps/docs/tools/pdf-to-image.md b/apps/docs/tools/pdf-to-image.md new file mode 100644 index 00000000..602c17ca --- /dev/null +++ b/apps/docs/tools/pdf-to-image.md @@ -0,0 +1,126 @@ +# PDF to Image + +Convert PDF pages to high-quality raster images. Supports page selection, multiple output formats, DPI control, and color modes. Includes info and preview sub-routes for inspecting PDFs before conversion. + +## API Endpoint + +`POST /api/v1/tools/pdf-to-image` + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| format | string | No | `"png"` | Output format: `png`, `jpg`, `webp`, `avif`, `tiff`, `gif`, `heic`, `heif`, `jxl` | +| dpi | number | No | 150 | Render resolution (36 to 2400). Higher DPI produces larger, more detailed images. | +| quality | number | No | 85 | Output quality for lossy formats (1 to 100) | +| colorMode | string | No | `"color"` | Color mode: `color`, `grayscale`, `bw` (black and white threshold) | +| pages | string | No | `"all"` | Page selection: `all`, single page (`3`), range (`1-5`), or comma-separated (`1,3,5-8`) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/pdf-to-image \ + -F "file=@document.pdf" \ + -F 'settings={"format":"png","dpi":300,"pages":"1-3","colorMode":"color"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "pageCount": 10, + "selectedPages": [1, 2, 3], + "format": "png", + "pages": [ + { + "page": 1, + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/page-1.png", + "size": 234567 + }, + { + "page": 2, + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/page-2.png", + "size": 198765 + }, + { + "page": 3, + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/page-3.png", + "size": 210456 + } + ], + "zipUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/pdf-pages.zip", + "zipSize": 612345 +} +``` + +## Info Sub-Route + +`POST /api/v1/tools/pdf-to-image/info` + +Returns the page count of a PDF without rendering any pages. + +### Info Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/pdf-to-image/info \ + -F "file=@document.pdf" +``` + +### Info Response + +```json +{ + "pageCount": 10 +} +``` + +## Preview Sub-Route + +`POST /api/v1/tools/pdf-to-image/preview` + +Returns low-resolution JPEG thumbnails of all pages as base64 data URLs. Useful for building a page selection UI. + +### Preview Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/pdf-to-image/preview \ + -F "file=@document.pdf" +``` + +### Preview Response + +```json +{ + "pageCount": 10, + "thumbnails": [ + { + "page": 1, + "dataUrl": "data:image/jpeg;base64,/9j/4AAQ...", + "width": 300, + "height": 424 + }, + { + "page": 2, + "dataUrl": "data:image/jpeg;base64,/9j/4AAQ...", + "width": 300, + "height": 424 + } + ] +} +``` + +## Notes + +- Uses MuPDF for PDF rendering, providing high-fidelity output with correct font rendering and vector graphics. +- Password-protected PDFs are not supported and will return a 400 error. +- The `pages` parameter supports flexible syntax: + - `"all"` or `""` - all pages + - `"3"` - single page + - `"1-5"` - page range (inclusive) + - `"1,3,5-8"` - mixed individual pages and ranges +- Page numbers are 1-based. Specifying pages beyond the document length returns a 400 error. +- The main endpoint always generates both individual page downloads and a ZIP containing all selected pages. +- The preview endpoint renders at 72 DPI and scales to 300px width for fast thumbnail generation. Thumbnails are JPEG at 60% quality. +- The preview endpoint respects the `MAX_PDF_PAGES` server configuration, limiting how many thumbnails are generated. +- For large documents at high DPI, processing time increases proportionally. Consider using lower DPI (150) for web use and higher DPI (300-600) for print. diff --git a/apps/docs/tools/qr-generate.md b/apps/docs/tools/qr-generate.md new file mode 100644 index 00000000..19ca38af --- /dev/null +++ b/apps/docs/tools/qr-generate.md @@ -0,0 +1,71 @@ +--- +description: Generate QR codes with custom colors and error correction levels. +--- + +# QR Code Generator + +Generate QR code images from text or URLs with configurable size, error correction level, and custom foreground/background colors. + +## API Endpoint + +`POST /api/v1/tools/qr-generate` + +Accepts a **JSON body** (not multipart). No file upload is needed. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| text | string | Yes | - | Content to encode in the QR code (1 to 2000 characters) | +| size | number | No | `400` | Output image width/height in pixels (100 to 10000) | +| errorCorrection | string | No | `"M"` | Error correction level: `L` (7%), `M` (15%), `Q` (25%), `H` (30%) | +| foreground | string | No | `"#000000"` | QR code foreground/module color in hex (`#RRGGBB`) | +| background | string | No | `"#FFFFFF"` | QR code background color in hex (`#RRGGBB`) | + +### Error Correction Levels + +| Level | Recovery | Use Case | +|-------|----------|----------| +| `L` | ~7% | Maximum data density | +| `M` | ~15% | Balanced (default) | +| `Q` | ~25% | Good for printed codes | +| `H` | ~30% | Best for codes with logos overlay | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/qr-generate \ + -H "Authorization: Bearer si_your-api-key" \ + -H "Content-Type: application/json" \ + -d '{"text": "https://snapotter.com", "size": 500, "errorCorrection": "H"}' +``` + +Branded QR code with custom colors: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/qr-generate \ + -H "Authorization: Bearer si_your-api-key" \ + -H "Content-Type: application/json" \ + -d '{"text": "Hello World", "size": 300, "foreground": "#1a365d", "background": "#f7fafc"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/qrcode.png", + "originalSize": 0, + "processedSize": 4520 +} +``` + +## Notes + +- This endpoint accepts JSON, not multipart form data, since no image upload is needed. +- The output is always a PNG image. +- The output filename is always `qrcode.png`. +- `originalSize` is always 0 since this tool generates images from scratch. +- A 2-module quiet zone (margin) is included around the QR code. +- Maximum text length is 2000 characters. Actual capacity depends on error correction level and character encoding. +- Higher error correction levels allow the QR code to remain scannable even if partially obscured but reduce data capacity. diff --git a/apps/docs/tools/red-eye-removal.md b/apps/docs/tools/red-eye-removal.md new file mode 100644 index 00000000..2e67ac27 --- /dev/null +++ b/apps/docs/tools/red-eye-removal.md @@ -0,0 +1,72 @@ +# Red Eye Removal + +AI-powered detection and correction of red eye caused by camera flash. + +## API Endpoint + +`POST /api/v1/tools/red-eye-removal` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `face-detection` (200-300 MB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| sensitivity | number | No | `50` | Red eye detection sensitivity (0-100). Higher values detect more subtle red-eye | +| strength | number | No | `70` | Correction strength (0-100). How aggressively to neutralize red | +| format | string | No | - | Output format (optional override) | +| quality | number | No | `90` | Output quality (1-100) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/red-eye-removal \ + -F "file=@flash-photo.jpg" \ + -F 'settings={"sensitivity":60,"strength":80}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Detecting red eyes...","percent":40} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/flash-photo_redeye_fixed.png", + "originalSize": 280000, + "processedSize": 290000, + "facesDetected": 2, + "eyesCorrected": 4 + } +} +``` + +## Notes + +- Requires the `face-detection` model bundle to be installed (200-300 MB). +- First detects faces, then locates eye regions within each face, and finally identifies and corrects red-eye pixels. +- The `facesDetected` count indicates how many faces were found; `eyesCorrected` is the total number of individual eyes that had red-eye corrected. +- Output is always PNG for maximum quality preservation. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/remove-background.md b/apps/docs/tools/remove-background.md new file mode 100644 index 00000000..6b64dcf4 --- /dev/null +++ b/apps/docs/tools/remove-background.md @@ -0,0 +1,129 @@ +# Remove Background + +AI-powered background removal with optional effects (blur, shadow, gradient, custom background). + +## API Endpoint + +`POST /api/v1/tools/remove-background` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `background-removal` (4-5 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| model | string | No | - | AI model variant to use | +| backgroundType | string | No | `"transparent"` | One of: `transparent`, `color`, `gradient`, `blur`, `image` | +| backgroundColor | string | No | - | Hex color for solid background | +| gradientColor1 | string | No | - | First gradient color | +| gradientColor2 | string | No | - | Second gradient color | +| gradientAngle | number | No | - | Gradient angle in degrees | +| blurEnabled | boolean | No | - | Enable background blur effect | +| blurIntensity | number | No | - | Blur intensity (0-100) | +| shadowEnabled | boolean | No | - | Enable drop shadow on subject | +| shadowOpacity | number | No | - | Shadow opacity (0-100) | +| outputFormat | string | No | - | Output format: `png`, `webp`, or `avif` | +| edgeRefine | integer | No | - | Edge refinement level (0-3) | +| decontaminate | boolean | No | - | Remove color bleed from edges | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/remove-background \ + -F "file=@photo.jpg" \ + -F 'settings={"backgroundType":"transparent","edgeRefine":2,"outputFormat":"png"}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Removing background...","percent":50} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/photo_mask.png", + "maskUrl": "/api/v1/download/{jobId}/photo_mask.png", + "originalUrl": "/api/v1/download/{jobId}/photo_original.png", + "originalSize": 245000, + "processedSize": 180000, + "filename": "photo.jpg", + "model": "rembg" + } +} +``` + +## Effects Endpoint (Phase 2) + +`POST /api/v1/tools/remove-background/effects` + +Re-applies background effects without re-running the AI model. Uses cached mask and original from Phase 1. + +### Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| settings | JSON | Yes | - | JSON with effect settings (see below) | +| backgroundImage | file | No | - | Custom background image (when backgroundType is `image`) | + +#### Settings JSON fields + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| jobId | string | Yes | Job ID from Phase 1 | +| filename | string | Yes | Original filename from Phase 1 | +| backgroundType | string | No | `transparent`, `color`, `gradient`, `blur`, `image` | +| backgroundColor | string | No | Hex color for solid background | +| gradientColor1 | string | No | First gradient color | +| gradientColor2 | string | No | Second gradient color | +| gradientAngle | number | No | Gradient angle in degrees | +| blurEnabled | boolean | No | Enable background blur | +| blurIntensity | number | No | Blur intensity (0-100) | +| shadowEnabled | boolean | No | Enable drop shadow | +| shadowOpacity | number | No | Shadow opacity (0-100) | +| outputFormat | string | No | `png`, `webp`, or `avif` | + +### Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/remove-background/effects \ + -F 'settings={"jobId":"a1b2c3d4-...","filename":"photo.jpg","backgroundType":"color","backgroundColor":"#FF5500","outputFormat":"png"}' +``` + +### Response (200 OK) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/photo_nobg.png", + "processedSize": 195000 +} +``` + +## Notes + +- Requires the `background-removal` model bundle to be installed (4-5 GB). +- Phase 1 caches the transparent mask and original image so that Phase 2 (effects) can re-apply different backgrounds instantly without re-running the AI model. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. +- EXIF rotation is auto-corrected before processing. diff --git a/apps/docs/tools/replace-color.md b/apps/docs/tools/replace-color.md new file mode 100644 index 00000000..1aed3c99 --- /dev/null +++ b/apps/docs/tools/replace-color.md @@ -0,0 +1,59 @@ +--- +description: Replace a specific color in an image with another color or make it transparent. +--- + +# Replace & Invert Color + +Replace pixels matching a source color with a target color, or make them transparent. Uses Euclidean distance in RGB space with configurable tolerance for smooth blending at color boundaries. + +## API Endpoint + +`POST /api/v1/tools/replace-color` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| sourceColor | string | No | `"#FF0000"` | Hex color to find (format: `#RRGGBB`) | +| targetColor | string | No | `"#00FF00"` | Hex color to replace with (format: `#RRGGBB`) | +| makeTransparent | boolean | No | `false` | Make matching pixels transparent instead of replacing with target color | +| tolerance | number | No | `30` | Color matching tolerance (0 to 255). Higher values match a wider range of similar colors | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/replace-color \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"sourceColor": "#FF0000", "targetColor": "#0000FF", "tolerance": 40}' +``` + +Make a green background transparent: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/replace-color \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@greenscreen.png" \ + -F 'settings={"sourceColor": "#00FF00", "makeTransparent": true, "tolerance": 50}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.png", + "originalSize": 2450000, + "processedSize": 2100000 +} +``` + +## Notes + +- Color matching uses Euclidean distance in RGB space, scaled by `tolerance * sqrt(3)`. +- Replacement blending is proportional to color distance: pixels closer to the source color receive more of the target color, creating smooth transitions. +- When `makeTransparent` is `true`, the output is forced to PNG (or WebP/AVIF) if the input format does not support alpha channels (e.g., JPEG). +- A tolerance of 0 matches only the exact source color. Higher values (50+) will match a broader range of similar hues. +- Output format matches the input format unless transparency is needed and the input format lacks alpha support. diff --git a/apps/docs/tools/resize.md b/apps/docs/tools/resize.md new file mode 100644 index 00000000..b18e6922 --- /dev/null +++ b/apps/docs/tools/resize.md @@ -0,0 +1,69 @@ +--- +description: Resize images by pixels, percentage, or with fit modes. +--- + +# Resize + +Resize images by specifying exact pixel dimensions, a percentage scale factor, or a fit mode that controls how the image adapts to the target dimensions. + +## API Endpoint + +`POST /api/v1/tools/resize` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| width | integer | No | - | Target width in pixels (max 16383) | +| height | integer | No | - | Target height in pixels (max 16383) | +| fit | string | No | `"contain"` | How the image fits the dimensions: `contain`, `cover`, `fill`, `inside`, `outside` | +| withoutEnlargement | boolean | No | `false` | Prevent upscaling if image is smaller than target | +| percentage | number | No | - | Scale by percentage (e.g. 50 for half size) | + +At least one of `width`, `height`, or `percentage` must be provided. + +### Fit Modes + +- **contain** - Resize to fit within the dimensions, preserving aspect ratio (may leave empty space) +- **cover** - Resize to cover the dimensions, preserving aspect ratio (may crop) +- **fill** - Stretch to exactly match dimensions (ignores aspect ratio) +- **inside** - Like `contain`, but only downscales, never upscales +- **outside** - Like `cover`, but only downscales, never upscales + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/resize \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"width": 800, "height": 600, "fit": "contain"}' +``` + +Resize by percentage: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/resize \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"percentage": 50}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 980000 +} +``` + +## Notes + +- Maximum dimension is 16383 pixels on either axis (Sharp/libvips limit). +- Output format matches the input format. HEIC, RAW, PSD, and SVG inputs are automatically decoded before processing. +- EXIF orientation is auto-applied before resizing. +- The `withoutEnlargement` flag is useful for batch processing where some images may already be smaller than the target. diff --git a/apps/docs/tools/restore-photo.md b/apps/docs/tools/restore-photo.md new file mode 100644 index 00000000..35d1ddfb --- /dev/null +++ b/apps/docs/tools/restore-photo.md @@ -0,0 +1,89 @@ +# Photo Restoration + +Fix scratches, tears, and damage on old photos using a multi-step AI pipeline. Combines scratch repair, face enhancement, denoising, and optional colorization. + +## API Endpoint + +`POST /api/v1/tools/restore-photo` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `photo-restoration` (800 MB - 1 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| scratchRemoval | boolean | No | `true` | Remove scratches and surface damage | +| faceEnhancement | boolean | No | `true` | Enhance faces in the restored photo | +| fidelity | number | No | `0.7` | Face enhancement fidelity (0-1). Higher values preserve original features more | +| denoise | boolean | No | `true` | Apply denoising to the restored result | +| denoiseStrength | number | No | `25` | Denoising strength (0-100) | +| colorize | boolean | No | `false` | Colorize the restored photo (for grayscale images) | +| colorizeStrength | number | No | `85` | Colorization intensity (0-100) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/restore-photo \ + -F "file=@damaged-old-photo.jpg" \ + -F 'settings={"scratchRemoval":true,"faceEnhancement":true,"fidelity":0.6,"colorize":true}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Removing scratches...","percent":30} +``` + +``` +event: progress +data: {"phase":"processing","stage":"Enhancing faces...","percent":60} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/damaged-old-photo_restored.jpg", + "previewUrl": "/api/v1/download/{jobId}/preview.webp", + "originalSize": 200000, + "processedSize": 350000, + "width": 1200, + "height": 900, + "steps": ["scratch_removal", "face_enhancement", "denoise", "colorize"], + "scratchCoverage": 12.5, + "facesEnhanced": 2, + "isGrayscale": true, + "colorized": true + } +} +``` + +## Notes + +- Requires the `photo-restoration` model bundle to be installed (800 MB - 1 GB). +- The pipeline runs multiple AI steps sequentially: scratch repair, face enhancement (GFPGAN), denoising, and optionally colorization. +- The `steps` array in the result shows which processing steps were actually executed. +- `scratchCoverage` is an estimated percentage of the image area that had scratch damage. +- `fidelity` controls how strongly faces are enhanced vs. preserving the original appearance. Lower values produce more aggressive enhancement; higher values are more conservative. +- The `colorize` option automatically detects if the image is grayscale. The `isGrayscale` flag in the result confirms this detection. +- Output format matches the input format automatically. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, HDR, and AVIF input formats via automatic decoding. diff --git a/apps/docs/tools/rotate.md b/apps/docs/tools/rotate.md new file mode 100644 index 00000000..d24bf055 --- /dev/null +++ b/apps/docs/tools/rotate.md @@ -0,0 +1,68 @@ +--- +description: Rotate images by any angle and flip horizontally or vertically. +--- + +# Rotate & Flip + +Rotate images by an arbitrary angle and/or flip them horizontally or vertically. Rotation and flip operations can be combined in a single request. + +## API Endpoint + +`POST /api/v1/tools/rotate` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| angle | number | No | `0` | Rotation angle in degrees (clockwise). Accepts any numeric value. | +| horizontal | boolean | No | `false` | Flip the image horizontally (mirror) | +| vertical | boolean | No | `false` | Flip the image vertically | + +## Example Request + +Rotate 90 degrees clockwise: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/rotate \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"angle": 90}' +``` + +Flip horizontally: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/rotate \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"horizontal": true}' +``` + +Rotate and flip together: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/rotate \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"angle": 45, "vertical": true}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 2480000 +} +``` + +## Notes + +- Rotation is applied first, then flip operations. +- Non-90-degree rotations (e.g. 45 degrees) will enlarge the canvas to fit the rotated image, with transparent or black fill depending on the output format. +- Common values: 90, 180, 270 for quarter-turn rotations. +- EXIF orientation is auto-applied before processing, so the rotation is relative to the visual orientation. diff --git a/apps/docs/tools/sharpening.md b/apps/docs/tools/sharpening.md new file mode 100644 index 00000000..f17d273d --- /dev/null +++ b/apps/docs/tools/sharpening.md @@ -0,0 +1,68 @@ +--- +description: Sharpen images using adaptive, unsharp mask, or high-pass methods with optional noise reduction. +--- + +# Sharpening + +Advanced sharpening tool with three methods: adaptive (smart edge-aware), unsharp mask (classic radius/amount), and high-pass (texture emphasis). Includes built-in noise reduction to prevent sharpening artifacts. + +## API Endpoint + +`POST /api/v1/tools/sharpening` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| method | string | No | `"adaptive"` | Sharpening algorithm: `adaptive`, `unsharp-mask`, `high-pass` | +| sigma | number | No | `1.0` | Adaptive: Gaussian sigma (0.5 to 10) | +| m1 | number | No | `1.0` | Adaptive: flat area sharpening (0 to 10) | +| m2 | number | No | `3.0` | Adaptive: jagged area sharpening (0 to 20) | +| x1 | number | No | `2.0` | Adaptive: flat/jagged threshold (0 to 10) | +| y2 | number | No | `12` | Adaptive: maximum flat sharpening (0 to 50) | +| y3 | number | No | `20` | Adaptive: maximum jagged sharpening (0 to 50) | +| amount | number | No | `100` | Unsharp mask: sharpening amount (0 to 1000) | +| radius | number | No | `1.0` | Unsharp mask: blur radius in pixels (0.1 to 5) | +| threshold | number | No | `0` | Unsharp mask: minimum brightness difference to sharpen (0 to 255) | +| strength | number | No | `50` | High-pass: filter strength (0 to 100) | +| kernelSize | number | No | `3` | High-pass: convolution kernel size (3 or 5) | +| denoise | string | No | `"off"` | Pre-sharpening noise reduction: `off`, `light`, `medium`, `strong` | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/sharpening \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"method": "adaptive", "sigma": 1.5}' +``` + +Unsharp mask with threshold to protect smooth areas: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/sharpening \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"method": "unsharp-mask", "amount": 150, "radius": 1.5, "threshold": 10}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 2510000 +} +``` + +## Notes + +- Only parameters relevant to the chosen method are used. For example, `amount`, `radius`, and `threshold` are ignored when `method` is `adaptive`. +- The adaptive method uses Sharp's built-in adaptive sharpening with configurable flat/jagged region behavior. +- The `denoise` option applies noise reduction before sharpening to prevent amplification of noise/grain. +- High-pass sharpening extracts fine detail by subtracting a blurred version from the original, then blending back. +- Output format matches the input format. HEIC, RAW, PSD, and SVG inputs are automatically decoded before processing. diff --git a/apps/docs/tools/smart-crop.md b/apps/docs/tools/smart-crop.md new file mode 100644 index 00000000..d80aaaf3 --- /dev/null +++ b/apps/docs/tools/smart-crop.md @@ -0,0 +1,67 @@ +# Smart Crop + +Smart subject-aware, face-aware, or trim-based cropping. Uses Sharp's attention/entropy strategies and AI face detection for intelligent framing. + +## API Endpoint + +`POST /api/v1/tools/smart-crop` + +**Processing:** Synchronous (uses `createToolRoute` factory, returns result directly) + +**Model bundle:** `face-detection` (200-300 MB) -- required only for `face` mode + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| mode | string | No | `"subject"` | Crop mode: `subject`, `face`, `trim`. (Legacy values `attention` and `content` map to `subject` and `trim`) | +| strategy | string | No | `"attention"` | Strategy for subject mode: `attention` or `entropy` | +| width | integer | No | - | Target width in pixels | +| height | integer | No | - | Target height in pixels | +| padding | integer | No | `0` | Padding percentage around subject (0-50) | +| facePreset | string | No | `"head-shoulders"` | Face framing preset: `closeup`, `head-shoulders`, `upper-body`, `half-body` | +| sensitivity | number | No | `0.5` | Face detection sensitivity (0-1) | +| threshold | integer | No | `30` | Trim mode threshold for background detection (0-255) | +| padToSquare | boolean | No | `false` | Pad trimmed result to a square | +| padColor | string | No | `"#ffffff"` | Background color for padding | +| targetSize | integer | No | - | Target size for padded output (pixels) | +| quality | integer | No | - | Output quality (1-100) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/smart-crop \ + -F "file=@portrait.jpg" \ + -F 'settings={"mode":"face","width":1080,"height":1080,"facePreset":"head-shoulders"}' +``` + +## Response (200 OK) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/portrait_smartcrop.jpg", + "originalSize": 500000, + "processedSize": 320000 +} +``` + +## Modes + +### Subject Mode +Uses Sharp's attention or entropy strategy to find the most visually interesting region and crops around it. + +### Face Mode +Detects faces using AI, then frames the crop around detected faces using the specified `facePreset`. Falls back to subject mode (attention strategy) if no faces are detected. + +### Trim Mode +Removes uniform borders/background from the image. Optionally pads the result to a square with a specified background color and target size. + +## Notes + +- This tool uses the synchronous `createToolRoute` factory, so it returns a standard response (not 202 async). +- Face mode requires the `face-detection` model bundle (200-300 MB). +- Subject and trim modes work without any AI model bundle. +- The `facePreset` determines how tightly the crop frames detected faces: `closeup` is the tightest, `half-body` is the widest. +- If no width/height are specified, defaults to 1080x1080. diff --git a/apps/docs/tools/split.md b/apps/docs/tools/split.md new file mode 100644 index 00000000..e148c439 --- /dev/null +++ b/apps/docs/tools/split.md @@ -0,0 +1,42 @@ +# Image Splitting + +Split a single image into grid tiles by column/row count or by specific pixel dimensions. Returns a ZIP archive containing all tiles. + +## API Endpoint + +`POST /api/v1/tools/split` + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| columns | integer | No | 3 | Number of columns to split into (1 to 100) | +| rows | integer | No | 3 | Number of rows to split into (1 to 100) | +| tileWidth | integer | No | - | Tile width in pixels (min 10). Overrides `columns` when both `tileWidth` and `tileHeight` are set. | +| tileHeight | integer | No | - | Tile height in pixels (min 10). Overrides `rows` when both `tileWidth` and `tileHeight` are set. | +| outputFormat | string | No | `"original"` | Output format for tiles: `original`, `png`, `jpg`, `webp`, `avif`, `jxl` | +| quality | number | No | 90 | Output quality for lossy formats (1 to 100) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/split \ + -F "file=@large-image.png" \ + -F 'settings={"columns":3,"rows":3,"outputFormat":"png"}' \ + --output split-tiles.zip +``` + +## Example Response + +The response is streamed directly as a ZIP file with `Content-Type: application/zip`. The filename follows the pattern `split-.zip`. + +Each tile inside the ZIP is named `_r_c.` (e.g. `photo_r1_c1.png`, `photo_r2_c3.webp`). + +## Notes + +- Accepts a single image file. +- Supports HEIC, RAW, PSD, and SVG input formats (automatically decoded). +- When both `tileWidth` and `tileHeight` are provided, they take priority over `columns`/`rows`. The grid dimensions are calculated as `ceil(imageWidth / tileWidth)` and `ceil(imageHeight / tileHeight)`. +- Edge tiles (rightmost column, bottom row) may be smaller than the specified tile size if the image dimensions are not evenly divisible. +- Maximum grid size is capped at 100x100 (10,000 tiles). +- The response streams the ZIP directly, so there is no JSON response body. Use `--output` with curl to save the file. diff --git a/apps/docs/tools/stitch.md b/apps/docs/tools/stitch.md new file mode 100644 index 00000000..3994e6cc --- /dev/null +++ b/apps/docs/tools/stitch.md @@ -0,0 +1,56 @@ +# Stitch / Combine + +Join multiple images side by side, stacked vertically, or arranged in a grid. Supports alignment, gap, border, corner radius, and multiple resize modes. + +## API Endpoint + +`POST /api/v1/tools/stitch` + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| direction | string | No | `"horizontal"` | Layout direction: `horizontal`, `vertical`, `grid` | +| gridColumns | integer | No | 2 | Number of columns when direction is `grid` (2 to 100) | +| resizeMode | string | No | `"fit"` | How images are resized: `fit`, `original`, `stretch`, `crop` | +| alignment | string | No | `"center"` | Cross-axis alignment: `start`, `center`, `end` | +| gap | number | No | 0 | Gap between images in pixels (0 to 1000) | +| border | number | No | 0 | Outer border width in pixels (0 to 500) | +| cornerRadius | number | No | 0 | Corner radius applied to final output (0 to 500) | +| backgroundColor | string | No | `"#FFFFFF"` | Background/border color as hex (e.g. `#FF0000`) | +| format | string | No | `"png"` | Output format: `png`, `jpeg`, `webp`, `avif`, `jxl` | +| quality | number | No | 90 | Output quality (1 to 100) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/stitch \ + -F "file=@image1.png" \ + -F "file=@image2.png" \ + -F "file=@image3.png" \ + -F 'settings={"direction":"horizontal","resizeMode":"fit","gap":10,"backgroundColor":"#FFFFFF","format":"png"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/stitch.png", + "originalSize": 1234567, + "processedSize": 987654 +} +``` + +## Notes + +- Requires at least 2 images. Upload multiple image files in the multipart request. +- Supports HEIC, RAW, PSD, and SVG input formats (automatically decoded). +- Resize modes: + - `fit` - Scale images to match the smallest dimension along the joining axis. + - `original` - Keep original sizes (may produce uneven edges). + - `stretch` - Force images to match the smallest dimension without preserving aspect ratio. + - `crop` - Cover-crop images to match the smallest dimension. +- In `grid` mode, cells are sized to the median dimensions of all images. +- The `cornerRadius` is applied to the entire final output, not individual images. +- Canvas size is limited by the `MAX_CANVAS_PIXELS` server configuration to prevent memory exhaustion. diff --git a/apps/docs/tools/strip-metadata.md b/apps/docs/tools/strip-metadata.md new file mode 100644 index 00000000..a473e234 --- /dev/null +++ b/apps/docs/tools/strip-metadata.md @@ -0,0 +1,110 @@ +--- +description: Remove EXIF, GPS, ICC, and XMP metadata from images for privacy and smaller file sizes. +--- + +# Remove Metadata + +Remove EXIF, GPS, ICC color profiles, and XMP metadata from images. Useful for privacy (removing GPS coordinates, camera info) and reducing file size. + +## API Endpoints + +### Strip Metadata + +`POST /api/v1/tools/strip-metadata` + +Processes the image and returns a cleaned version with selected metadata removed. + +### Inspect Metadata + +`POST /api/v1/tools/strip-metadata/inspect` + +Returns the parsed metadata as JSON without modifying the image. Useful for previewing what metadata exists before stripping. + +## Parameters (Strip) + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| stripExif | boolean | No | `false` | Remove EXIF data (camera settings, dates, etc.) | +| stripGps | boolean | No | `false` | Remove GPS/location data only | +| stripIcc | boolean | No | `false` | Remove ICC color profile | +| stripXmp | boolean | No | `false` | Remove XMP metadata (Adobe, IPTC) | +| stripAll | boolean | No | `true` | Remove all metadata at once | + +When `stripAll` is `true`, it overrides the individual flags and removes everything. + +## Example Request + +Strip all metadata: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/strip-metadata \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"stripAll": true}' +``` + +Strip only GPS data (keep camera info and color profile): + +```bash +curl -X POST http://localhost:1349/api/v1/tools/strip-metadata \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"stripAll": false, "stripGps": true}' +``` + +Inspect metadata without modifying: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/strip-metadata/inspect \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" +``` + +## Example Response (Strip) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 2380000 +} +``` + +## Example Response (Inspect) + +```json +{ + "filename": "photo.jpg", + "fileSize": 2450000, + "exif": { + "Make": "Canon", + "Model": "EOS R5", + "DateTimeOriginal": "2024:03:15 14:30:00", + "ExposureTime": "1/250", + "FNumber": 2.8, + "ISO": 400 + }, + "gps": { + "GPSLatitudeRef": "N", + "GPSLatitude": [37, 46, 30], + "_latitude": 37.775, + "_longitude": -122.4183 + }, + "icc": { + "Profile Size": "3144 bytes", + "Color Space": "RGB", + "Description": "sRGB IEC61966-2.1" + }, + "xmp": { + "CreatorTool": "Adobe Photoshop 25.0" + } +} +``` + +## Notes + +- The image is re-encoded in its original format after stripping. JPEG uses mozjpeg at quality 90, PNG uses compression level 9, WebP uses quality 85. +- Stripping ICC profiles may cause subtle color shifts if the image was tagged with a non-sRGB profile. Use `stripIcc: false` if color accuracy matters. +- The inspect endpoint parses GPS coordinates into decimal latitude/longitude values (prefixed with underscore) for convenience. +- Supported input formats: JPEG, PNG, WebP, AVIF, TIFF, GIF. diff --git a/apps/docs/tools/svg-to-raster.md b/apps/docs/tools/svg-to-raster.md new file mode 100644 index 00000000..92b86d9d --- /dev/null +++ b/apps/docs/tools/svg-to-raster.md @@ -0,0 +1,78 @@ +# SVG to Raster + +Convert SVG files to raster image formats (PNG, JPEG, WebP, AVIF, TIFF, GIF, HEIF, or JXL) at custom resolution and DPI. Also supports batch conversion of multiple SVGs. + +## API Endpoint + +`POST /api/v1/tools/svg-to-raster` + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| width | integer | No | - | Target width in pixels (1 to 65536). Maintains aspect ratio if only one dimension set. | +| height | integer | No | - | Target height in pixels (1 to 65536). Maintains aspect ratio if only one dimension set. | +| dpi | integer | No | 300 | Render DPI, controls the base rasterization density (36 to 2400) | +| quality | number | No | 90 | Output quality for lossy formats (1 to 100) | +| backgroundColor | string | No | `"#00000000"` | Background color as hex (6 or 8 characters, 8-char includes alpha) | +| outputFormat | string | No | `"png"` | Output format: `png`, `jpg`, `webp`, `avif`, `tiff`, `gif`, `heif`, `jxl` | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/svg-to-raster \ + -F "file=@logo.svg" \ + -F 'settings={"width":1024,"dpi":300,"outputFormat":"png","backgroundColor":"#FFFFFF"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/logo.png", + "previewUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/preview.webp", + "originalSize": 12345, + "processedSize": 67890 +} +``` + +## Batch Endpoint + +`POST /api/v1/tools/svg-to-raster/batch` + +Convert multiple SVG files in one request. Returns a ZIP archive. + +### Additional Batch Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| clientJobId | string | No | - | Optional client-provided job ID for progress tracking (max 128 chars) | + +### Batch Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/svg-to-raster/batch \ + -F "file=@icon1.svg" \ + -F "file=@icon2.svg" \ + -F "file=@icon3.svg" \ + -F 'settings={"width":512,"outputFormat":"png","dpi":150}' +``` + +### Batch Response + +The batch endpoint streams a ZIP file directly with headers: +- `Content-Type: application/zip` +- `X-Job-Id: ` +- `X-File-Results: ` + +## Notes + +- Only accepts SVG and SVGZ files (validates content, not just extension). SVGZ is automatically decompressed. +- SVG content is sanitized before rendering to prevent XSS and external resource loading. +- The `dpi` setting controls the density at which the SVG is rasterized. Higher DPI produces larger pixel dimensions from the same SVG viewport. +- When both `width` and `height` are provided, the image is resized using `fit: inside` (maintains aspect ratio within the bounds). +- A `previewUrl` is included in the response for formats that browsers cannot display natively (TIFF, HEIF). The preview is a 1200px WebP thumbnail. +- The default background `#00000000` is fully transparent. Set to `#FFFFFF` for a white background (useful with JPEG output which does not support transparency). +- Batch processing respects the `MAX_BATCH_SIZE` server configuration and uses concurrent workers for performance. +- Progress for batch operations can be tracked via SSE at `/api/v1/jobs/:jobId/progress`. diff --git a/apps/docs/tools/text-overlay.md b/apps/docs/tools/text-overlay.md new file mode 100644 index 00000000..dbc97af4 --- /dev/null +++ b/apps/docs/tools/text-overlay.md @@ -0,0 +1,63 @@ +--- +description: Add styled text overlays with drop shadows and background boxes. +--- + +# Text Overlay + +Add styled text to images with optional drop shadow and semi-transparent background box. Suitable for titles, captions, or annotations on photos. + +## API Endpoint + +`POST /api/v1/tools/text-overlay` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| text | string | Yes | - | Text to overlay (1 to 500 characters) | +| fontSize | number | No | `48` | Font size in pixels (8 to 200) | +| color | string | No | `"#FFFFFF"` | Text color in hex format (`#RRGGBB`) | +| position | string | No | `"bottom"` | Vertical placement: `top`, `center`, `bottom` | +| backgroundBox | boolean | No | `false` | Show a semi-transparent background rectangle behind the text | +| backgroundColor | string | No | `"#000000"` | Background box color in hex format (`#RRGGBB`) | +| shadow | boolean | No | `true` | Apply a drop shadow behind the text | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/text-overlay \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"text": "Hello World", "fontSize": 64, "color": "#FFFFFF", "position": "bottom", "shadow": true}' +``` + +With a background box: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/text-overlay \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"text": "Caption", "fontSize": 36, "position": "bottom", "backgroundBox": true, "backgroundColor": "#000000"}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 2470000 +} +``` + +## Notes + +- Text is always centered horizontally within the image. +- The drop shadow uses a 2px offset with 3px blur at 70% black opacity. +- The background box spans the full image width at 70% opacity, with height proportional to the font size (1.8x). +- Text is rendered via SVG composite, so the system's default sans-serif font is used. +- XML-special characters in the text are safely escaped. +- Output format matches the input format. HEIC, RAW, PSD, and SVG inputs are automatically decoded before processing. diff --git a/apps/docs/tools/transparency-fixer.md b/apps/docs/tools/transparency-fixer.md new file mode 100644 index 00000000..8149fef9 --- /dev/null +++ b/apps/docs/tools/transparency-fixer.md @@ -0,0 +1,71 @@ +# PNG Transparency Fixer + +Fix fake transparent PNGs in one click. Uses AI matting (BiRefNet HR Matting model) to produce true alpha transparency, with defringe post-processing to clean up edges. + +## API Endpoint + +`POST /api/v1/tools/transparency-fixer` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `background-removal` (4-5 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| defringe | number | No | `30` | Defringe intensity (0-100). Removes semi-transparent fringe pixels around edges | +| outputFormat | string | No | `"png"` | Output format: `png` or `webp` | +| removeWatermark | boolean | No | `false` | Apply watermark removal pre-processing (median filter) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/transparency-fixer \ + -F "file=@fake-transparent.png" \ + -F 'settings={"defringe":40,"outputFormat":"png"}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Processing transparency...","percent":50} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/fake-transparent_fixed.png", + "originalSize": 180000, + "processedSize": 150000, + "filename": "fake-transparent.png" + } +} +``` + +## Notes + +- Requires the `background-removal` model bundle to be installed (4-5 GB). +- Uses `birefnet-hr-matting` as the primary model for high-quality alpha matting. Falls back to `birefnet-general` if the HR model runs out of memory. +- The `defringe` option removes semi-transparent fringe pixels that AI matting sometimes leaves around hair, fur, and fine edges. It works by blurring the alpha channel and zeroing out low-confidence pixels. +- The `removeWatermark` option applies a median filter pre-processing step. It is a basic watermark reduction, not a dedicated watermark removal tool. +- Only outputs PNG or lossless WebP (both support alpha transparency). +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/upscale.md b/apps/docs/tools/upscale.md new file mode 100644 index 00000000..e10a90fc --- /dev/null +++ b/apps/docs/tools/upscale.md @@ -0,0 +1,76 @@ +# Image Upscaling + +AI super-resolution enhancement using Real-ESRGAN. Upscales images 2x-4x while preserving detail. + +## API Endpoint + +`POST /api/v1/tools/upscale` + +**Processing:** Asynchronous (returns 202, poll `/api/v1/jobs/{jobId}/progress` for status via SSE) + +**Model bundle:** `upscale-enhance` (4-5 GB) + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| file | file | Yes | - | Image file (multipart) | +| scale | number | No | `2` | Upscale factor (e.g., 2, 3, 4) | +| model | string | No | `"auto"` | Model to use (e.g., `auto`, specific model names) | +| faceEnhance | boolean | No | `false` | Apply face enhancement during upscaling | +| denoise | number | No | `0` | Denoising strength (0 = off) | +| format | string | No | `"auto"` | Output format: `auto`, `png`, `jpg`, `webp`, `tiff`, `gif`, `avif`, `heic`, `heif`, `jxl` | +| quality | number | No | `95` | Output quality (1-100) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/upscale \ + -F "file=@photo.jpg" \ + -F 'settings={"scale":4,"model":"auto","faceEnhance":true,"format":"png"}' +``` + +## Response + +### Initial Response (202 Accepted) + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "async": true +} +``` + +### Progress (SSE at `/api/v1/jobs/{jobId}/progress`) + +``` +event: progress +data: {"phase":"processing","stage":"Upscaling...","percent":60} +``` + +### Final Result (via SSE) + +```json +{ + "phase": "complete", + "percent": 100, + "result": { + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/{jobId}/photo_4x.png", + "previewUrl": "/api/v1/download/{jobId}/preview.webp", + "originalSize": 120000, + "processedSize": 2400000, + "width": 4096, + "height": 4096, + "method": "realesrgan-x4plus" + } +} +``` + +## Notes + +- Requires the `upscale-enhance` model bundle to be installed (4-5 GB). +- Uses Real-ESRGAN when available; falls back to Lanczos interpolation if the AI model is unavailable. +- The `faceEnhance` option applies GFPGAN face restoration during upscaling for better face quality. +- For non-browser-previewable output formats (HEIC, JXL, TIFF), a WebP preview is generated alongside the main output. +- Supports HEIC/HEIF, RAW, TGA, PSD, EXR, and HDR input formats via automatic decoding. diff --git a/apps/docs/tools/vectorize.md b/apps/docs/tools/vectorize.md new file mode 100644 index 00000000..842ef9c1 --- /dev/null +++ b/apps/docs/tools/vectorize.md @@ -0,0 +1,57 @@ +# Image to SVG + +Vectorize raster images into SVG using tracing algorithms. Supports black-and-white tracing (potrace) and full-color multi-layer vectorization. + +## API Endpoint + +`POST /api/v1/tools/vectorize` + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| colorMode | string | No | `"bw"` | Tracing mode: `bw` (black and white) or `color` (multi-color layers) | +| threshold | number | No | 128 | Brightness threshold for B&W mode (0 to 255). Pixels below become black. | +| colorPrecision | number | No | 6 | Color quantization precision for color mode (1 to 16). Higher values produce more distinct color layers. | +| layerDifference | number | No | 6 | Minimum color difference between layers in color mode (1 to 128) | +| filterSpeckle | number | No | 4 | Minimum area for traced shapes in pixels (1 to 256). Removes noise/speckles. | +| pathMode | string | No | `"spline"` | Path smoothing: `none` (jagged), `polygon` (straight segments), `spline` (smooth curves) | +| cornerThreshold | number | No | 60 | Angle threshold for corner detection in color mode (0 to 180 degrees) | +| invert | boolean | No | `false` | Invert the image before tracing (swap black/white) | + +## Example Request + +```bash +curl -X POST http://localhost:13490/api/v1/tools/vectorize \ + -F "file=@logo.png" \ + -F 'settings={"colorMode":"bw","threshold":128,"filterSpeckle":4,"pathMode":"spline"}' +``` + +### Color Vectorization + +```bash +curl -X POST http://localhost:13490/api/v1/tools/vectorize \ + -F "file=@illustration.png" \ + -F 'settings={"colorMode":"color","colorPrecision":8,"layerDifference":6,"filterSpeckle":4}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/logo.svg", + "originalSize": 45678, + "processedSize": 12345 +} +``` + +## Notes + +- Output is always an SVG file regardless of input format. +- Supports HEIC, RAW, PSD, and SVG input formats (automatically decoded to raster before tracing). +- B&W mode uses the potrace algorithm. The image is converted to grayscale first, then thresholded to pure black/white before tracing. +- Color mode uses a multi-layer approach: the image is quantized into color layers, each traced separately and stacked in the SVG output. +- Lower `filterSpeckle` values preserve more detail but produce larger SVG files with more paths. +- The `pathMode` setting significantly affects file size: `none` produces the most paths, `spline` produces the smoothest (and usually smallest) output. +- For best results with logos and icons, use B&W mode with a clean high-contrast input. For photographs or illustrations, use color mode with higher `colorPrecision`. diff --git a/apps/docs/tools/watermark-image.md b/apps/docs/tools/watermark-image.md new file mode 100644 index 00000000..e7355488 --- /dev/null +++ b/apps/docs/tools/watermark-image.md @@ -0,0 +1,58 @@ +--- +description: Overlay a logo or image as a watermark with configurable position, opacity, and scale. +--- + +# Image Watermark + +Overlay a logo or secondary image as a watermark on a base image. The watermark is scaled relative to the base image width and positioned at a corner or center. + +## API Endpoint + +`POST /api/v1/tools/watermark-image` + +Accepts multipart form data with **two** image files and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| position | string | No | `"bottom-right"` | Watermark placement: `center`, `top-left`, `top-right`, `bottom-left`, `bottom-right` | +| opacity | number | No | `50` | Watermark opacity percentage (0 to 100) | +| scale | number | No | `25` | Watermark width as percentage of main image width (1 to 100) | + +### File Fields + +| Field Name | Required | Description | +|------------|----------|-------------| +| file | Yes | The main/base image | +| watermark | Yes | The watermark/logo image | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/watermark-image \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F "watermark=@logo.png" \ + -F 'settings={"position": "bottom-right", "opacity": 60, "scale": 20}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 2520000 +} +``` + +## Notes + +- Both images are validated and decoded (HEIC, RAW, PSD, SVG supported). +- The watermark is resized proportionally so its width equals `scale`% of the main image width. +- Opacity is applied via an alpha mask composited with `dest-in` blending. +- Corner positions use a 20px padding from the image edge. +- If the watermark image has transparency (e.g., a PNG logo), it is preserved during compositing. +- EXIF orientation is auto-applied on both images before processing. diff --git a/apps/docs/tools/watermark-text.md b/apps/docs/tools/watermark-text.md new file mode 100644 index 00000000..e2a2dcf4 --- /dev/null +++ b/apps/docs/tools/watermark-text.md @@ -0,0 +1,62 @@ +--- +description: Add text watermarks with configurable position, opacity, rotation, and tiling. +--- + +# Text Watermark + +Add a text watermark overlay to images. Supports single placement at corners/center or tiled repetition across the entire image, with configurable font size, color, opacity, and rotation. + +## API Endpoint + +`POST /api/v1/tools/watermark-text` + +Accepts multipart form data with an image file and a JSON `settings` field. + +## Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| text | string | Yes | - | Watermark text (1 to 500 characters) | +| fontSize | number | No | `48` | Font size in pixels (8 to 1000) | +| color | string | No | `"#000000"` | Text color in hex format (`#RRGGBB`) | +| opacity | number | No | `50` | Text opacity percentage (0 to 100) | +| position | string | No | `"center"` | Placement: `center`, `top-left`, `top-right`, `bottom-left`, `bottom-right`, `tiled` | +| rotation | number | No | `0` | Text rotation angle in degrees (-360 to 360) | + +## Example Request + +```bash +curl -X POST http://localhost:1349/api/v1/tools/watermark-text \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"text": "SAMPLE", "fontSize": 64, "opacity": 30, "position": "center", "rotation": -30}' +``` + +Tiled watermark across the entire image: + +```bash +curl -X POST http://localhost:1349/api/v1/tools/watermark-text \ + -H "Authorization: Bearer si_your-api-key" \ + -F "file=@photo.jpg" \ + -F 'settings={"text": "DRAFT", "fontSize": 36, "opacity": 20, "position": "tiled", "rotation": -45}' +``` + +## Example Response + +```json +{ + "jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", + "downloadUrl": "/api/v1/download/a1b2c3d4-e5f6-7890-abcd-ef1234567890/photo.jpg", + "originalSize": 2450000, + "processedSize": 2480000 +} +``` + +## Notes + +- The watermark is rendered as SVG text and composited onto the image, preserving output quality. +- Tiled mode spaces text elements based on font size (6x horizontal, 4x vertical spacing), capped at 500 elements maximum. +- For corner positions, padding from the edge equals the font size. +- The font used is the system's default sans-serif font. +- XML-special characters in the text (`&`, `<`, `>`, `"`, `'`) are safely escaped. +- Output format matches the input format. HEIC, RAW, PSD, and SVG inputs are automatically decoded before processing. diff --git a/apps/landing/public/llms.txt b/apps/landing/public/llms.txt index feec5570..8f2bff68 100644 --- a/apps/landing/public/llms.txt +++ b/apps/landing/public/llms.txt @@ -1,6 +1,6 @@ # SnapOtter -> Open-source, self-hostable image manipulation suite with 52 tools. Single Docker container, no external services. Dual-licensed AGPLv3 / commercial. +> Open-source, self-hostable image manipulation suite with 53 tools. Single Docker container, no external services. Dual-licensed AGPLv3 / commercial. SnapOtter runs entirely on your own infrastructure. Images are processed locally with no data sent to external APIs. Deploy once with Docker and use it as much as you need. diff --git a/llms.txt b/llms.txt new file mode 100644 index 00000000..9d09379b --- /dev/null +++ b/llms.txt @@ -0,0 +1,141 @@ +# SnapOtter + +Open-source, self-hostable image manipulation suite with 53 tools. Single Docker container, no external services required. Dual-licensed AGPLv3 and commercial. + +All image processing happens locally. Images never leave your infrastructure. Supports 55+ input formats (including 23 camera RAW formats) and 14 output formats. + +## Quick Start (Docker) + +docker run -d --name snapotter -p 1349:1349 -v snapotter-data:/data snapotter/snapotter:latest + +Default credentials: admin / admin (must change on first login). +Add --gpus all for NVIDIA GPU acceleration on AI tools. +Multi-arch: AMD64 and ARM64 (Intel, Apple Silicon, Raspberry Pi). + +## Tools by Category + +### Essentials +- resize: Resize by pixels, percentage, or social media presets +- crop: Freeform crop, aspect ratio presets, shape crop +- rotate: Rotate, flip, and straighten images +- convert: Convert between image formats +- compress: Reduce file size by quality or target size + +### Optimization +- optimize-for-web: Smart format selection and quality tuning for web delivery +- strip-metadata: Remove EXIF, GPS, and camera info for privacy +- edit-metadata: View and edit EXIF, IPTC, and XMP fields +- bulk-rename: Pattern-based batch renaming with variables +- image-to-pdf: Combine images into multi-page PDFs +- favicon: Generate all favicon and app icon sizes from one image + +### Adjustments +- adjust-colors: Brightness, contrast, saturation, temperature, and effects +- sharpening: Adaptive, unsharp mask, and high-pass sharpening +- replace-color: Swap colors with tolerance control or invert +- color-blindness: Simulate 8 types of color vision deficiency + +### AI Tools (local ML, no cloud APIs) +- remove-background: AI background removal (rembg) +- upscale: 2x/4x AI super-resolution (RealESRGAN) +- erase-object: AI inpainting to remove unwanted objects (LaMa) +- ocr: Extract text from images (PaddleOCR, 80+ languages) +- blur-faces: AI face detection with blur for GDPR compliance +- smart-crop: Subject-aware and face-centered cropping +- image-enhancement: One-click auto-fix for exposure, contrast, color +- enhance-faces: AI face restoration for blurry or low-res portraits +- colorize: Convert B&W photos to full color with AI +- noise-removal: AI denoising for ISO noise, JPEG artifacts, grain +- red-eye-removal: Automatic red-eye detection and correction +- restore-photo: AI repair of scratches, tears, and damage +- passport-photo: Compliant passport/ID photos for 30+ countries +- content-aware-resize: Seam carving that preserves subjects +- ai-canvas-expand: Outpainting to extend image borders with AI +- transparency-fixer: Fix PNGs with fake transparent backgrounds + +### Watermark and Overlay +- watermark-text: Add text watermarks with tiling and opacity +- watermark-image: Overlay logos with position and opacity control +- text-overlay: Add styled text, captions, and titles +- compose: Layer and composite multiple images with blending +- meme-generator: Create memes with custom text and templates + +### Utilities +- info: View dimensions, EXIF, GPS, color space, file details +- compare: Side-by-side image comparison with slider overlay +- find-duplicates: Perceptual hashing for near-duplicate detection +- color-palette: Extract dominant colors as hex/RGB values +- qr-generate: Custom QR codes with colors, logos, and patterns +- html-to-image: Capture webpages as high-quality images +- barcode-read: Decode QR, Code 128, EAN-13 and more from images +- image-to-base64: Generate data URIs for HTML/CSS embedding + +### Layout and Composition +- collage: 25+ grid templates for photo collages +- stitch: Join images side by side, stacked, or in grids +- split: Cut images into grid tiles for Instagram and print +- border: Add borders, rounded corners, and drop shadows +- beautify: Screenshot mockups with device frames and backgrounds + +### Format and Conversion +- svg-to-raster: Convert SVG to PNG/JPEG/WebP at custom scale/DPI +- vectorize: Trace raster images to SVG vector paths +- gif-tools: Resize, optimize, reverse, extract GIF frames +- pdf-to-image: Convert PDF pages to images at custom DPI + +## REST API + +Base URL: http://localhost:1349/api/v1 +Interactive docs: http://localhost:1349/api/docs (Swagger UI) +Machine-readable spec: http://localhost:1349/api/docs/json (OpenAPI 3.1) +LLM-optimized spec: http://localhost:1349/llms.txt (generated from OpenAPI at runtime) + +Authentication methods: +- Session cookie (browser login) +- API key header: X-API-Key: si_ +- Bearer token: Authorization: Bearer si_ + +API keys support scoped permissions that intersect with user role permissions. + +Processing a file: + POST /api/v1/tools/:toolId + Content-Type: multipart/form-data + Body: file (binary), settings (JSON string) + Response: { jobId, downloadUrl, originalSize, processedSize } + +AI tools return 202 Accepted and stream progress via SSE: + GET /api/v1/jobs/:jobId/progress (EventSource) + +Pipelines (chained tool workflows): + GET /api/v1/pipelines + POST /api/v1/pipelines + POST /api/v1/pipelines/:id/run + +Batch processing: submit multiple files, receive a ZIP archive. + +## Tech Stack + +Frontend: React 19, Vite 6, Tailwind CSS 4, Zustand +Backend: Fastify 5, Sharp (image processing), SQLite via Drizzle ORM +AI/ML: Python sidecar (rembg, RealESRGAN, PaddleOCR, MediaPipe, LaMa) +Auth: Session-based with OIDC/SSO support (Google, GitHub, Okta, any OIDC provider) +Languages: 21 locales with RTL support + +## Key Features + +- Pipelines: chain tools into reusable workflows, import/export as JSON +- Batch processing: process hundreds of files at once +- Image editor: layer-based editor with brushes, shapes, filters, curves +- OIDC/SSO: Google, GitHub, Okta, or any OpenID Connect provider +- Role-based access: admin, editor, user roles with 14 granular permissions +- Custom roles and API key scoping + +## Links + +- Source code: https://github.com/snapotter-hq/snapotter +- Documentation: https://docs.snapotter.com +- Live demo: https://demo.snapotter.com +- Docker Hub: https://hub.docker.com/r/snapotter/snapotter +- GHCR: ghcr.io/snapotter-hq/snapotter +- Discord: https://discord.gg/hr3s7HPUsr +- Website: https://snapotter.com diff --git a/tests/e2e-docs/homepage.spec.ts b/tests/e2e-docs/homepage.spec.ts index 2d1ab040..10869bcf 100644 --- a/tests/e2e-docs/homepage.spec.ts +++ b/tests/e2e-docs/homepage.spec.ts @@ -29,7 +29,7 @@ test.describe("Docs Homepage", () => { test("features section renders all 6 feature cards", async ({ page }) => { const features = [ - "52 Image Tools", + "53 Image Tools", "Local AI", "Pipelines", "REST API", diff --git a/tests/e2e-landing/homepage.spec.ts b/tests/e2e-landing/homepage.spec.ts index f07ed51c..d435015f 100644 --- a/tests/e2e-landing/homepage.spec.ts +++ b/tests/e2e-landing/homepage.spec.ts @@ -59,7 +59,7 @@ test.describe("Landing Homepage", () => { test("bento grid renders with search and tool count", async ({ page }) => { await expect(page.getByText("50+ tools. Zero cloud dependency.")).toBeVisible(); await expect(page.getByPlaceholder("Search tools...")).toBeVisible(); - await expect(page.getByText(/Showing 52 of 52 tools/)).toBeVisible(); + await expect(page.getByText(/Showing 53 of 53 tools/)).toBeVisible(); }); test("enterprise section renders feature cards", async ({ page }) => { diff --git a/tests/e2e-landing/interactions.spec.ts b/tests/e2e-landing/interactions.spec.ts index 1d07a579..56a4a713 100644 --- a/tests/e2e-landing/interactions.spec.ts +++ b/tests/e2e-landing/interactions.spec.ts @@ -9,28 +9,28 @@ test.describe("BentoGrid Interactions", () => { const input = page.getByPlaceholder("Search tools..."); await input.fill("resize"); await expect(page.getByText("Resize", { exact: true }).first()).toBeVisible(); - await expect(page.getByText(/Showing \d+ of 52 tools/)).toBeVisible(); + await expect(page.getByText(/Showing \d+ of 53 tools/)).toBeVisible(); }); test("category pill filters tools", async ({ page }) => { await page.getByText(/AI Tools/).click(); - await expect(page.getByText(/Showing 16 of 52 tools/)).toBeVisible(); + await expect(page.getByText(/Showing 16 of 53 tools/)).toBeVisible(); await expect(page.getByText("Remove Background")).toBeVisible(); }); test("clicking All resets category filter", async ({ page }) => { await page.getByText(/AI Tools/).click(); - await expect(page.getByText(/Showing 16 of 52 tools/)).toBeVisible(); + await expect(page.getByText(/Showing 16 of 53 tools/)).toBeVisible(); - await page.getByText(/All \(52\)/).click(); - await expect(page.getByText(/Showing 52 of 52 tools/)).toBeVisible(); + await page.getByText(/All \(53\)/).click(); + await expect(page.getByText(/Showing 53 of 53 tools/)).toBeVisible(); }); test("search with no results shows empty state", async ({ page }) => { const input = page.getByPlaceholder("Search tools..."); await input.fill("xyznonexistent"); await expect(page.getByText("No tools found. Try a different search.")).toBeVisible(); - await expect(page.getByText(/Showing 0 of 52 tools/)).toBeVisible(); + await expect(page.getByText(/Showing 0 of 53 tools/)).toBeVisible(); }); test("combined search and category filter works", async ({ page }) => {