Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TwinFrame

TwinFrame

A command-line tool that detects visually similar or duplicate images in a folder using perceptual hashing (PDQ). Unlike byte-level comparison, perceptual hashing finds images that look the same even if they differ in compression, resolution, or minor edits.

Why perceptual hashing

A regular checksum (MD5, SHA-256) only tells you if two files are byte-identical. Resize an image by one pixel, re-save it as a different quality JPEG, or strip its EXIF data, and the checksum changes completely even though the picture looks the same to a human.

Perceptual hashing solves this by hashing what the image looks like rather than its raw bytes. TwinFrame uses PDQ, which produces a 256-bit fingerprint that stays stable across resizing, re-encoding, and minor edits. Two images with a small Hamming distance between their hashes are very likely the same picture.

To find matches quickly across large collections, hashes are indexed in a BK-tree, which supports fast approximate nearest-neighbor lookups under a distance metric instead of requiring an exhaustive pairwise comparison.

Features

  • Recursively scans a folder and automatically detects image files by content, not extension
  • Computes a PDQ perceptual hash for every image
  • Groups likely duplicates using a tolerance-based Hamming distance search
  • Prints a clear terminal report showing which file to keep and which are recoverable space
  • Optional HTML report with a visual side-by-side of each duplicate group
  • Fast: BK-tree indexing avoids comparing every image against every other image

Installation

Clone the repository and build with Cargo:

git clone https://github.com/mathieuemery/twinframe.git
cd twinframe
cargo build --release

The compiled binary will be at target/release/twinframe.

Usage

twinframe --folder-path <PATH> [--report] [--tolerance]

Tolerance is the maximum Hamming distance between two hashes for them to be considered duplicates. Lower values mean stricter matching (fewer false positives, but you might miss heavily edited duplicates); higher values catch more variations at the risk of grouping unrelated images. The default is 15, which works well for resized or re-compressed copies of the same image.

HTML report

Running with --report writes a report.html file to the current directory. It includes:

  • A summary of total images scanned and potential space savings.
  • Collapsible duplicate groups with image previews, dimensions, file size, and creation date.
  • KEEP / DELETE labels, with the largest file in each group suggested as the one to keep.
  • A "Copy path" button per file for quick access.
  • Dark mode support.

Note: Image previews in the HTML report use absolute filesystem paths and will only render correctly when the report is opened locally on the same machine.

All options

Flag Short Description
--folder-path -f Path to the folder to analyze (required)
--report -r Generate an HTML report (report.html in the current directory)
--tolerance -t Define the tolerance to use [default: 15]
--help -h Show help
--version -V Show version

Both relative and absolute paths are accepted. The path is resolved to its absolute form before scanning, so all reported file paths are always fully qualified.

Example output

======================================================================
                      DUPLICATE DETECTION REPORT                      
======================================================================

GROUP #1: (Hash: e3b3cb37...)
[KEEP]  -> "/home/user/Pictures/test.png" (282.54 KB)
[DELETE] -> "/home/user/Pictures/test copy.png" (282.54 KB)
[DELETE] -> "/home/user/Pictures/test_compressed.png" (65.15 KB)
----------------------------------------------------------------------

▶ Est. Recoverable Space: 0.34 MB
======================================================================

Within each group, the largest file is kept as the reference copy and the rest are flagged for deletion, TwinFrame never deletes anything itself, it only reports.

How it works

  1. Walk the folder: walkdir recursively traverses the directory, and each file is opened and checked to confirm it's actually a decodable image, regardless of its extension.
  2. Hash each image: every image is passed through PDQ, producing a 256-bit hash that captures its visual structure.
  3. Index the hashes: hashes are inserted into a BK-tree, a structure built for fast lookups under a distance metric.
  4. Find matches: for each hash, TwinFrame queries the tree for every other hash within the configured tolerance, then groups those files together.
  5. Report: groups are sorted by file size, with the largest file suggested as the one to keep, and a terminal (and optionally HTML) report is generated.

Known limitations

  • Single-threaded analysis: large folders with thousands of images will be slow. Parallel hashing via rayon is a planned improvement.
  • HTML report output path: the report is always written to report.html in the current working directory. A --output flag is planned.

Dependencies

Crate Purpose
pdqhash Perceptual hash generation
bk-tree Nearest-neighbor search by Hamming distance
image Image decoding
walkdir Recursive directory traversal
clap CLI argument parsing
askama HTML report templating
chrono Timestamp formatting
anyhow Error handling
hex Hash display

License

MIT

Contributing

Issues and pull requests are welcome. If you run into an image that isn't detected correctly, please include the file and its format in your report.

About

Finds near-duplicate images with PDQ perceptual hashing.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages