Skip to main content

VisualDedup 🔍✨

CI License: MIT Python: 3.9+

VisualDedup is a high-performance Python toolkit designed to find exact and visually similar images in large collections, cluster them into duplicate groups, explain why they match, recommend which copy to retain, and safely clean datasets without destructive surprises.

Existing libraries such as imagededup and Perception provide basic perceptual hashing. VisualDedup goes beyond basic hashing to solve the real operational challenges of large-scale dataset management:

  1. Explains Why Images Match: Classifies transformations (resizing, JPEG recompression, format conversion, and cropping).
  2. Sub-Quadratic Indexing: Uses BK-Trees and Multi-Index Hamming Hashing to eliminate the $O(N^2)$ pairwise comparison bottleneck, scaling to hundreds of thousands of images.
  3. Intelligent Keeper Recommendation: Recommends the optimal file to retain based on resolution, lossless format preservation, compression quality, and original timestamps.
  4. Safe Dataset Cleanup: Non-destructive by default with strict dry-run modes, quarantine isolation, and full JSON audit logs.
  5. Interactive Visual Reports: Generates standalone HTML dashboards and image contact sheets with side-by-side previews, difference metrics, and keeper badges.
  6. Incremental SQLite Cache: Avoids recomputing hashes on subsequent scans by tracking file modification times and byte sizes.

⚡ Quickstart

1. Installation

# Clone the repository
git clone https://github.com/your-org/visualdedup.git
cd visualdedup

# Create virtual environment and install
python -m venv .venv
# Windows:
.venv\Scripts\activate
# Linux/macOS:
source .venv/bin/activate

pip install -e ".[dev]"

💻 Command-Line Interface (CLI)

Scan a Folder and Generate Reports

# Scan with pHash (DCT) and export JSON + interactive HTML reports
visualdedup scan ./photos --output report.json --html report.html

# Scan with custom Hamming threshold and contact sheets
visualdedup scan ./dataset --hash phash --threshold 8 --contact-sheet sheet.png

# Fast candidate indexing with Multi-Index Hashing for large collections
visualdedup scan ./large_collection --index multi_index --keeper best_quality

# Output structured tabular CSV for data pipelines / Excel
visualdedup scan ./dataset --csv report.csv

# Output pure pipeable JSON directly to stdout
visualdedup scan ./dataset --json | jq .

# Store duplicate images in a separate folder organized by cluster subfolders
visualdedup scan ./dataset --store-duplicates ./duplicates_output

# Move duplicate discards into a separate folder (cleaning the source directory)
visualdedup scan ./dataset --move-duplicates ./quarantine_folder

Inspect Two Images Side-by-Side

# Terminal table view:
visualdedup inspect original.png compressed_downscaled.jpg

# Structured JSON view for scripts and CI:
visualdedup inspect original.png compressed_downscaled.jpg --json

Outputs a detailed comparison table with SHA-256, dHash, pHash, aHash, wHash distances, resolution differences, and an automated diagnosis (e.g. Resized copy: downscaled ~2.0x; JPEG recompression).

Safe Dataset Cleanup

# Dry run simulation (default: does not touch files)
visualdedup clean ./dataset --mode quarantine --dry-run

# Execute quarantine move with confirmation
visualdedup clean ./dataset --mode quarantine --target-dir ./quarantine --confirm

🐍 Python Library API

from visualdedup import VisualDedup, KeeperStrategy, CleanMode

# 1. Initialize engine with incremental caching and sub-quadratic indexing
vd = VisualDedup(
    cache_path=".visualdedup_cache.db",
    hash_type="phash",       # "phash", "dhash", "ahash", "whash", "exact"
    threshold=8,             # Max Hamming distance
    index_type="bktree",     # "bktree" or "multi_index"
)

# 2. Scan folders
clusters, summary = vd.scan(["./dataset"], recursive=True)

print(f"Scanned {summary.total_scanned} files.")
print(f"Found {summary.total_clusters} duplicate clusters.")
print(f"Reclaimable space: {summary.reclaimable_bytes / (1024 * 1024):.2f} MB")

# 3. Inspect keeper recommendations
for cluster in clusters:
    print(f"\nCluster {cluster.cluster_id} ({cluster.match_category}):")
    print(f"  Keeper: {cluster.keeper_decision.keeper_path}")
    print(f"  Reason: {cluster.keeper_decision.reason}")
    print(f"  Discards: {cluster.keeper_decision.discard_paths}")

# 4. Export visual reports
vd.export_html(clusters, summary, "report.html")
vd.export_json(clusters, summary, "report.json")

# 5. Execute safe cleanup (e.g. quarantine mode with dry run)
clean_result = vd.clean(
    clusters,
    mode=CleanMode.QUARANTINE,
    target_dir="./quarantine_folder",
    dry_run=True,
    audit_log_path="audit.json"
)

🚀 Performance & Sub-Quadratic Indexing

Naive deduplication compares every image pair ($O(N^2)$ comparisons). For 5,000 images, that requires 12.5 million comparisons.

VisualDedup implements metric BK-Trees and Multi-Index Hamming Hashing (Pigeonhole block search):

Dataset Size Pairwise Checks $N(N-1)/2$ Naive Linear $O(N^2)$ BK-Tree $O(N \log N)$ Multi-Index Hash Speedup
500 124,750 31 ms 38 ms 4 ms 7.8x
1,000 499,500 116 ms 124 ms 8 ms 14.5x
2,500 3,123,750 697 ms 619 ms 20 ms 34.8x
5,000 12,497,500 3,000 ms 2,088 ms 59 ms 50.5x

Run the benchmarks yourself:

python benchmarks/benchmark_indexing.py

🛡️ Keeper Selection Strategies

Strategy Criteria Best For
best_quality (default) Resolution > Lossless format (PNG > JPEG) > File size > Original timestamp General dataset cleaning
highest_resolution Maximizes pixel dimensions ($W \times H$) Upscaled/downscaled collections
smallest_file Minimizes storage space Bandwidth / disk-constrained archives
largest_file Preserves least compressed files Photography & print workflows
oldest Retains earliest file by modification date Digital asset origin preservation
newest Retains most recently modified file Continuous syncing backups

🧪 Testing

pytest -v

All 22 unit and integration tests validate hashing invariance, tree search pruning, cluster disjoint sets, transformation explanations, safe deletion guards, and report exports.


📄 License

VisualDedup is distributed under the MIT License.

Metadata

Release files for visualdedup 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for visualdedup 0.2.0
File Size Uploaded Provenance
visualdedup-0.2.0.tar.gz 41.5 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for visualdedup 0.2.0
File Interpreter ABI Platform
visualdedup-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 81.6 kB

Release files / visualdedup-0.2.0.tar.gz

Download URL visualdedup-0.2.0.tar.gz
Size 41.5 kB
Tags Source
SHA-256 checksum
How to use checksums
8731eb6a33cd0d3cb1f17d6e9ea306e94c33fb6c831e3adae8a7067580f8eff3
BLAKE2b-256 checksum
How to use checksums
137670731b62dff9320c2ed4033f417382438e61d86b2d7ab42756316a6ec043
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Release files / visualdedup-0.2.0-py3-none-any.whl

Download URL visualdedup-0.2.0-py3-none-any.whl
Size 40.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
edfe609b3d67fe0d09c6fd98bf45185b8795ba3fee5d19e9c342bfcfe18386a5
BLAKE2b-256 checksum
How to use checksums
c2e992d29714f797751fdbd939956eee1dca7ca3fd37069af0441d67e9213d79
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.10

Security for visualdedup 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page