Rust implementation of PhotoDNA perceptual hashing for image similarity detection
  • Rust 61.8%
  • Python 38.2%
Find a file
2026-03-23 16:30:47 +01:00
.github/workflows change: python release only on pyo3 crate tagging 2026-03-23 15:26:44 +01:00
photo-dna change: badges in readme 2026-03-23 16:30:47 +01:00
python change: badges in readme 2026-03-23 16:30:47 +01:00
.gitignore add: initial implementation 2026-03-20 16:34:15 +01:00
Cargo.lock add: initial implementation 2026-03-20 16:34:15 +01:00
Cargo.toml add: msrv to Cargo.toml files 2026-03-23 15:23:53 +01:00
LICENSE Initial commit 2026-03-20 16:02:20 +01:00
README.md change: message in readme 2026-03-23 15:58:09 +01:00
rustfmt.toml add: initial implementation 2026-03-20 16:34:15 +01:00

PhotoDNA: Perceptual Image Hashing in Rust and Python

A pure Rust implementation and Python bindings of the PhotoDNA perceptual hashing algorithm for image similarity detection.

Overview

PhotoDNA provides robust image hashing and comparison capabilities with:

  • Pure Rust implementation for maximum performance and safety
  • Python bindings via PyO3 for easy integration
  • Perceptual hashing that detects similar images even after transformations
  • Multiple distance metrics for flexible similarity comparison

Packages

Rust Crate: photo-dna

The core implementation in pure Rust.

cargo add photo-dna

Python Package: photo-dna-rs

Python bindings using PyO3.

pip install photo-dna-rs

🐍 Quick Start (Python)

from photo_dna_rs import Hash

# Create hashes from images
hash1 = Hash.from_image_path("image1.jpg")
hash2 = Hash.from_image_path("image2.jpg")

# Calculate similarity (0.0 = different, 1.0 = identical)
similarity = hash1.similarity_log2p(hash2)
print(f"Images are {similarity*100:.1f}% similar")

# Convert between formats
hex_string = hash1.to_hex_str()
hash_from_hex = Hash.from_hex_str(hex_string)

🦀 Quick Start (Rust)

use photo_dna::Hash;

// Create hashes from image files
let hash1 = Hash::from_image_path("tests/image_1.jpg").unwrap();
let hash2 = Hash::from_image_path("tests/image_2.jpg").unwrap();

// Calculate similarity using log2p metric (recommended)
let similarity = hash1.similarity_log2p(&hash2);
println!("Image similarity: {:.2}%", similarity * 100.0);

// Serialize to hex string
let hex_string = hash1.to_hex_string();
let restored_hash = Hash::from_hex_str(&hex_string).unwrap();

Development

Building

# Build both crates
cargo build --all

# Build Python package
cd python
maturin develop

Testing

# Test Rust crate
cargo test --all-features

# Test Python package
cd python
pytest tests/ -v

Contributing

Contributions are welcome! Please follow these guidelines:

  1. Fork the repository and create a new branch
  2. Write tests for new functionality
  3. Update documentation for any changes
  4. Run tests: cargo test and pytest tests/ -v
  5. Format code: Use rustfmt for Rust code

🙏 Acknowledgments

This implementation is based on the excellent work by ArcaneNibble in the Open Alleged PhotoDNA project. The original reference implementation provided the foundation for this Rust port.

Support

  • GitHub Issues: Report bugs and request features
  • Pull Requests: Contribute improvements

License

This project is licensed under GPLv3. See the LICENSE file for details.


Built with ❤️ by the AIL Project for robust image analysis.