Files
SnapOtter/apps/docs/tools/find-duplicates.md
T
SnapOtter 7db6bb97da docs: add per-tool API documentation and fix parity gaps
- Create 53 per-tool VitePress documentation pages with accurate
  parameters from Zod schemas, example requests, and response formats
- Add root llms.txt for LLM-friendly repo browsing
- Fix OpenAPI spec: add auth and 422 error schemas to
  edit-metadata/inspect and strip-metadata/inspect sub-routes
- Fix tool count inconsistency (52 -> 53) across landing site,
  e2e tests, and local docs
- Rename color-adjustments.ts to adjust-colors.ts to match tool ID
- Update VitePress sidebar with all 8 tool categories and top nav
2026-06-08 11:52:28 +08:00

3.8 KiB

description
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

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

{
  "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).