A lightweight, two-stage near-duplicate image detection system combining fast perceptual hashing with deep learning embeddings for accurate and efficient duplicate detection at scale.
This project implements a two-stage funnel approach to near-duplicate detection:
- Stage 1 - dHash Sieve: Fast perceptual hashing using Difference Hash (dHash) to quickly filter obvious non-duplicates with O(1) comparisons
- Stage 2 - SSCD Verification: Deep learning-based verification using Meta's Self-Supervised Copy Detection (SSCD) model with FAISS indexing for accurate similarity scoring
This architecture balances speed (hash-based filtering) with accuracy (neural network verification), making it suitable for real-world applications.
- π Fast Filtering: dHash sieve eliminates ~99% of candidates in milliseconds
- π― High Accuracy: SSCD embeddings catch semantic duplicates (crops, filters, compression)
- π Scalable Search: FAISS vector index enables efficient similarity search over millions of images
- πΌοΈ Multiple UIs: Streamlit web app and FastAPI REST API
- π§ Configurable Thresholds: Tune sensitivity for your use case
- π§ͺ Evaluation Tools: Scripts for threshold tuning and large-scale benchmarking
Near-Image-Duplicate-Detection/
βββ app.py # FastAPI REST API server
βββ streamlit_app.py # Streamlit web interface
βββ main.py # CLI example usage
βββ requirements.txt # Python dependencies
β
βββ src/ # Core library
β βββ config.py # Configuration (paths, thresholds)
β βββ pipeline.py # DuplicateDetector orchestration
β βββ sieves.py # dHash computation & Hamming distance
β βββ verifier.py # SSCD model wrapper
β βββ indexer.py # FAISS index management
β βββ build_index.py # Script to build FAISS index
β βββ data_loader.py # Data loading utilities
β
βββ scripts/ # Utility scripts
β βββ compare_pair.py # Compare two images directly
β βββ tune_thresholds.py # Find optimal thresholds
β βββ evaluate_with_distractors.py # Large-scale evaluation
β
βββ data/ # Data directory
β βββ download_gldv2.py # Download GLDv2 distractor images
β βββ downoad_copydays.py # Download COPYDAYS benchmark
β βββ generate_attacks.py # Generate synthetic augmentations
β βββ processed/ # SSCD model & FAISS index
β βββ raw/ # Raw image datasets
β βββ synthetic_attacks/ # Generated test images
β βββ uploads/ # User uploads (runtime)
β
βββ tests/ # Unit tests
βββ test_indexer.py
βββ test_sieve.py
βββ test_verifier.py
- Python 3.8+
- ~2GB disk space for model and sample data
# Clone the repository
git clone https://github.com/yourusername/Near-Image-Duplicate-Detection.git
cd Near-Image-Duplicate-Detection
# Create virtual environment (recommended)
python -m venv venv
source venv/bin/activate # Linux/Mac
# or
.\venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txtPlace the SSCD TorchScript model at data/processed/sscd.pt. You can download it from Meta's SSCD repository.
First, populate data/raw/copydays/original/ with your reference images, then build the index:
python src/build_index.pyThis extracts SSCD embeddings for all images and stores them in a FAISS index for fast retrieval.
streamlit run streamlit_app.pyOpen the browser link, upload an image, and view:
- Top-K similar matches with similarity scores
- Duplicate/not-duplicate classification
- Visual comparison of query vs matches
uvicorn app:app --reloadThen visit http://localhost:8000 for the web interface or use the API:
curl -X POST "http://localhost:8000/api/detect" \
-F "file=@your_image.jpg"python main.pyOr compare two specific images:
python scripts/compare_pair.py path/to/image1.jpg path/to/image2.jpgEdit src/config.py to customize:
# SSCD Model
SSCD_MODEL_PATH = "data/processed/sscd.pt"
SSCD_INPUT_SIZE = 288
SSCD_SIM_THRESHOLD = 0.2 # Similarity threshold for duplicates
# dHash Sieve
HASH_HAMMING_THRESHOLD = 15 # Max Hamming distance for sieve pass
# Data Locations
IMAGE_DIR = "data/raw/copydays/original"
UPLOAD_DIR = "data/uploads"
# Search Settings
TOP_K = 10 # Number of results to returnQuery Image
β
βΌ
βββββββββββββββ
β Compute β Fast: ~1ms
β dHash β
βββββββββββββββ
β
βΌ
βββββββββββββββ
β Sieve β Compare against hash DB
β (Hamming) β Filter candidates with dist > threshold
βββββββββββββββ
β
βΌ
βββββββββββββββ
β Extract β ~50ms per image
β SSCD Embed β
βββββββββββββββ
β
βΌ
βββββββββββββββ
β FAISS β Cosine similarity search
β Search β Returns top-K matches
βββββββββββββββ
β
βΌ
Results
- Resizes image to 9x8 grayscale
- Computes horizontal gradient (each pixel vs right neighbor)
- Produces 64-bit hash
- Hamming distance measures similarity (lower = more similar)
- Meta's state-of-the-art copy detection model
- Trained on augmented image pairs
- 512-dimensional embeddings
- Robust to crops, filters, compression, overlays
Find optimal thresholds for your dataset:
python scripts/tune_thresholds.pyThis analyzes duplicate vs non-duplicate pairs and suggests threshold values.
Test with distractor images to measure real-world performance:
# Download distractor images (~10,000 images)
python data/download_gldv2.py
# Generate synthetic attacks (augmented versions)
python data/generate_attacks.py
# Run evaluation
python scripts/evaluate_with_distractors.py| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Web interface |
/api/detect |
POST | Upload image, get duplicate detection results |
/preview?path=... |
GET | Preview an image by path |
from src.pipeline import DuplicateDetector, build_hash_db
from src.config import IMAGE_DIR
# Initialize detector
hash_db = build_hash_db(IMAGE_DIR)
detector = DuplicateDetector(image_dir=IMAGE_DIR, hash_db=hash_db)
# Detect duplicates
result = detector.detect("query_image.jpg", top_k=5)
# Result structure:
# {
# "is_duplicate": bool,
# "stage": "sieve" | "verifier",
# "match": "path/to/match.jpg",
# "sieve_matches": [...],
# "verifier_matches": [...]
# }- torch / torchvision: Deep learning framework
- faiss-cpu: Vector similarity search
- imagehash: Perceptual hashing
- Pillow: Image processing
- streamlit: Web UI framework
- FastAPI: REST API framework
- albumentations: Image augmentation (for evaluation)
- opencv-python: Image processing utilities
# Run all tests
pytest tests/
# Run specific test file
pytest tests/test_sieve.py -v| Stage | Time per Image | Purpose |
|---|---|---|
| dHash | ~1ms | Fast filtering |
| SSCD Embedding | ~50ms (CPU) | Feature extraction |
| FAISS Search | ~1ms | Similarity lookup |
Typical end-to-end latency: 50-100ms per query on CPU.
MIT License - See LICENSE file for details.