Skip to content

Commit

Permalink
Add DifferenceHash implemnentations
Browse files Browse the repository at this point in the history
  • Loading branch information
takebayashi committed Jan 3, 2024
1 parent ec5c5eb commit d4bea57
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 0 deletions.
55 changes: 55 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
//! ## Supported Algorithms
//!
//! - Average Hash (aHash)
//! - Difference Hash (dHash)
//!
//! ## Usage
//!
Expand Down Expand Up @@ -88,6 +89,60 @@ pub fn average_hash(image: &image::DynamicImage, op: &ImageOp) -> Vec<bool> {
pixels.iter().map(|&v| v as u16 > average).collect()
}

/// Provides difference hash (dHash) calculation.
pub struct DifferenceHash<'a> {
op: &'a ImageOp,
}

impl<'a> DifferenceHash<'a> {
/// Creates a new `DifferenceHasher` with default parameters.
pub fn new() -> Self {
DifferenceHash::default()
}

/// Creates a new `DifferenceHasher` with the specified parameters.
pub fn with_op(op: &'a ImageOp) -> Self {
DifferenceHash { op }
}

/// Calculates difference hash (dHash) of the image and returns as a hex string.
pub fn hash(&self, image: &image::DynamicImage) -> String {
let bits = difference_hash(image, self.op);
let bytes = bits_to_bytes(&bits);
bytes_to_hex(&bytes)
}
}

impl Default for DifferenceHash<'_> {
/// Creates a new `DifferenceHasher` with default parameters.
fn default() -> Self {
DifferenceHash {
op: &ImageOp {
width: 9,
height: 8,
filter: FilterType::Lanczos3,
},
}
}
}

/// Calculates difference hash (dHash) of the image.
pub fn difference_hash(image: &image::DynamicImage, op: &ImageOp) -> Vec<bool> {
let preprocessed = image
.grayscale()
.resize_exact(op.width as u32, op.height as u32, op.filter);
let pixels = preprocessed.into_luma8().into_raw();
let mut bits = vec![false; ((op.width - 1) * op.height) as usize];
for y in 0..op.height {
for x in 0..op.width - 1 {
let offset_p = (y * op.width + x) as usize;
let offset_b = (y * (op.width - 1) + x) as usize;
bits[offset_b] = pixels[offset_p + 1] > pixels[offset_p];
}
}
bits
}

fn bits_to_bytes(bits: &[bool]) -> Vec<u8> {
let mut bytes = vec![0; (bits.len() + 7) / 8];
for (i, bit) in bits.iter().enumerate() {
Expand Down
14 changes: 14 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,17 @@ fn test_average_hash_2() {
let result = AverageHash::default().hash(&dynimg);
assert_eq!(result, "fff7e7e3c3000000");
}

#[test]
fn test_difference_hash_1() {
let dynimg = image::open("tests/1.jpg").unwrap();
let result = DifferenceHash::default().hash(&dynimg);
assert_eq!(result, "e0e0f0c4c6d290c0");
}

#[test]
fn test_difference_hash_2() {
let dynimg = image::open("tests/2.jpg").unwrap();
let result = DifferenceHash::default().hash(&dynimg);
assert_eq!(result, "ededcc860b0c19b6");
}

0 comments on commit d4bea57

Please sign in to comment.