| Crates.io | photodna |
| lib.rs | photodna |
| version | 1.5.1 |
| created_at | 2026-01-07 19:13:02.75526+00 |
| updated_at | 2026-01-07 19:13:02.75526+00 |
| description | Safe, high-level Rust bindings for the Microsoft PhotoDNA Edge Hash Generator |
| homepage | |
| repository | https://github.com/your-org/photodna-rs |
| max_upload_size | |
| id | 2028868 |
| size | 56,952 |
Safe, high-level Rust bindings for the Microsoft PhotoDNA Edge Hash Generator.
PhotoDNA is a perceptual hashing technology that creates a compact "fingerprint" of an image. This fingerprint can be used to identify visually similar images even after modifications like resizing, cropping, color adjustments, or format conversion.
This crate provides a safe, ergonomic Rust API on top of the low-level photodna-sys bindings.
Hash type uses a fixed-size stack array (no heap allocation)PhotoDnaErrorGeneratorOptions and HashOptionsThis crate requires the proprietary Microsoft PhotoDNA SDK.
Before building, you must set the PHOTODNA_SDK_ROOT environment variable to point to the SDK installation directory:
export PHOTODNA_SDK_ROOT=/path/to/PhotoDNA.EdgeHashGeneration-1.05.001
See the photodna-sys crate documentation for detailed setup instructions.
use photodna::{Generator, GeneratorOptions, Hash, Result};
fn main() -> Result<()> {
// Initialize the generator (loads the PhotoDNA library)
let generator = Generator::new(GeneratorOptions::default())?;
// Load your image as raw RGB pixels (e.g., using the `image` crate)
let image_data: Vec<u8> = load_image_as_rgb("photo.jpg");
let width = 640;
let height = 480;
// Compute the hash
let hash: Hash = generator.compute_hash_rgb(&image_data, width, height)?;
// Use the hash (e.g., store in database as hex)
println!("Hash: {}", hash.to_hex());
Ok(())
}
The main entry point. Manages the PhotoDNA library lifecycle:
use photodna::{Generator, GeneratorOptions};
let generator = Generator::new(
GeneratorOptions::new()
.max_threads(4)
)?;
// Check library version
println!("PhotoDNA v{}", generator.library_version_text().unwrap_or("?"));
A fixed-size (924 bytes) perceptual hash with zero-copy semantics:
use photodna::Hash;
// Format as hex for storage
let hex: String = hash.to_hex();
// Parse from hex
let hash = Hash::from_hex(&hex).unwrap();
// Access raw bytes
let bytes: &[u8] = hash.as_bytes();
// Use as HashMap key (implements Hash trait)
use std::collections::HashSet;
let mut seen = HashSet::new();
seen.insert(hash);
Multiple pixel formats are supported:
use photodna::{HashOptions, PixelFormat};
let options = HashOptions::new()
.pixel_format(PixelFormat::Bgra) // Common in Windows
.remove_border(true); // Auto-detect and remove borders
Supported formats:
Rgb, Bgr (3 bytes/pixel)Rgba, Bgra, Argb, Abgr (4 bytes/pixel)Gray8 (1 byte/pixel), Gray32 (4 bytes/pixel)Cmyk, YCbCr, Yuv420pAutomatically detect and remove borders from images:
let result = generator.compute_hash_with_border_detection(
&image_data,
width,
height,
HashOptions::default(),
)?;
println!("Primary hash: {}", result.primary.to_hex());
if let Some(borderless) = result.borderless {
println!("Borderless hash: {}", borderless.to_hex());
if let Some((x, y, w, h)) = result.content_region {
println!("Content region: {}x{} at ({}, {})", w, h, x, y);
}
}
All operations return Result<T, PhotoDnaError>:
use photodna::{PhotoDnaError, Result};
fn process_image(data: &[u8], w: u32, h: u32) -> Result<()> {
match generator.compute_hash_rgb(data, w, h) {
Ok(hash) => println!("Success: {}", hash),
Err(PhotoDnaError::ImageTooSmall) => {
eprintln!("Image must be at least 50x50 pixels");
}
Err(PhotoDnaError::ImageIsFlat) => {
eprintln!("Image has insufficient detail for hashing");
}
Err(e) => return Err(e),
}
Ok(())
}
Generator implements Send but not Sync. Options for concurrent use:
// Option 1: One generator per thread
let generator = Generator::new(GeneratorOptions::new().max_threads(1))?;
// Option 2: Shared with Mutex
use std::sync::{Arc, Mutex};
let generator = Arc::new(Mutex::new(Generator::new(GeneratorOptions::default())?));
// Option 3: Use max_threads for internal parallelism
let generator = Generator::new(GeneratorOptions::new().max_threads(8))?;
// Internal operations can use up to 8 threads
| Platform | Architecture | Support |
|---|---|---|
| Windows | x86_64, x86, arm64 | ✅ Native library |
| Linux | x86_64, x86, arm64 | ✅ Native library |
| macOS | x86_64, arm64 | ✅ Native library |
| BSD | any | 🔧 WebAssembly (via photodna-sys) |
This crate is licensed under MIT OR Apache-2.0.
Note: The PhotoDNA library itself is proprietary software from Microsoft. Usage of PhotoDNA requires a separate license agreement with Microsoft.