1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
#![deny(missing_docs)] //! This crate allows a `dhash` signature to be constructed from an image //! //! Requires the `image` crate //! //! A `dhash` is a differential gradient hash that compares the difference in gradient between adjacent pixels, and provides a 64 bit signature of an image. //! //! //! A `dhash` can be used to compare against other images for similarity and is resilient to differences in: //! //! * Aspect Ratio //! * Image Size //! * Brightness and Contrast //! //! //! //! Implementation details taken from the [Kind of Like That](http://www.hackerfactor.com/blog/?/archives/529-Kind-of-Like-That.html) blog //! ## Usage (CLI) //! //! Install this crate: //! ```bash //! cargo install dhash //! ``` //! //! Run `dhash <img1>` to print out a `dhash` of the image at path `img1` //! //! ```bash //! $ dhash test.jpg //! dhash for test.jpg is `13547707017824698364` //! ``` //! //! Run `dhash <img1> <img2>` to print out a `dhash` of both images and the distance between them (a lower number is closer): //! //! ```bash //! $ dhash test.jpg other.jpg //! dhash for test.jpg is `4485936524854165493` //! dhash for other.jpg is `3337201687795727957` //! distance is: 11 //! ``` //! use image::imageops::{grayscale, resize, FilterType}; use image::{GenericImageView, ImageBuffer}; use image::{Luma, Pixel}; const IMG_SCALE: u32 = 8; /// Computes the `dhash` value of a given image /// /// A `dhash` is a signature of an image that can be compared to other images /// /// Requires the `image` crate for loading in the image /// /// # Example /// /// ```no_run /// # use dhash::get_dhash; /// # fn main() { /// let img = image::open("test.jpg").expect("Could not open image"); /// let dhash = get_dhash(&img); /// # } /// ``` /// pub fn get_dhash<I: GenericImageView + 'static>(img: &I) -> u64 { let buffered_image = to_grey_signature_image(img); let mut bits: [bool; (IMG_SCALE * IMG_SCALE) as usize] = [false; (IMG_SCALE * IMG_SCALE) as usize]; let mut cur_value = 0; for i in 0..IMG_SCALE { for j in 0..IMG_SCALE { let left_pixel = buffered_image.get_pixel(i, j); let right_pixel = buffered_image.get_pixel(i + 1, j); bits[cur_value] = left_pixel[0] > right_pixel[0]; cur_value += 1; } } let mut value = 0; for i in 0..bits.len() { if bits[i] { value += 1 << i; } } return value; } /// Converts the image to a `dhash` image /// /// Returns an image that is a 9x8 grayscale image so the pixels can be used in comparison /// /// Used internally by the `get_dhash` method and is not normally needed to be called directly /// /// /// # Example /// /// ```no_run /// # use dhash::to_grey_signature_image; /// # fn main() { /// let img = image::open("test.jpg").expect("Could not open image"); /// let grey_signature_image = to_grey_signature_image(&img); /// # } /// ``` pub fn to_grey_signature_image<I: GenericImageView + 'static>( img: &I, ) -> ImageBuffer< Luma<<<I as GenericImageView>::Pixel as Pixel>::Subpixel>, std::vec::Vec<<<I as GenericImageView>::Pixel as Pixel>::Subpixel>, > { let grey_image = grayscale(img); let signature_image = resize(&grey_image, IMG_SCALE + 1, IMG_SCALE, FilterType::Triangle); return signature_image; } /// Returns the Hamming Distance between two `dhashes` /// /// The closer this number is to 0, the more similar the images are. With 0 being an exact match /// /// # Example /// ``` /// # use dhash::hamming_distance; /// # fn main() { /// let distance = hamming_distance(4485936524854165493, 3337201687795727957); /// assert_eq!(distance, 11); /// # } /// ``` pub fn hamming_distance(left: u64, right: u64) -> u32 { (left ^ right).count_ones() }