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
//! A module for comparing images. // from rust // from external crate // from local crate use error::RasterResult; use Image; use editor::{self, ResizeMode}; /// Compare two images and returns a hamming distance. A value of 0 indicates a likely similar /// picture. A value between 1 and 10 is potentially a variation. A value greater than 10 is /// likely a different image. /// /// # Examples /// ``` /// use raster::compare; /// /// let image1 = raster::open("tests/in/sample.jpg").unwrap(); /// let image2 = raster::open("tests/in/sample.png").unwrap(); /// /// let hamming_distance = compare::similar(&image1, &image2).unwrap(); /// println!("{}", hamming_distance); /// ``` pub fn similar(image1: &Image, image2: &Image) -> RasterResult<u8> { let bin1 = diff_hash(image1)?; let bin2 = diff_hash(image2)?; let mut distance = 0; for (index, value) in bin1.iter().enumerate() { if value != &bin2[index] { distance += 1; } } Ok(distance) } /// Compare if two images are equal. It will compare if the two images are of the same width and /// height. If the dimensions differ, it will return false. If the dimensions are equal, it will /// loop through each pixels. If one of the pixel don't match, it will return false. The pixels /// are compared using their RGB (Red, Green, Blue) values. /// /// # Examples /// ``` /// use raster::compare; /// /// let image1 = raster::open("tests/in/sample.png").unwrap(); /// let image2 = raster::open("tests/in/sample.png").unwrap(); /// /// let equal = compare::equal(&image1, &image2).unwrap(); /// assert_eq!(true, equal); /// ``` pub fn equal(image1: &Image, image2: &Image) -> RasterResult<bool> { // Check if image dimensions are equal if image1.width != image2.width || image1.height != image2.height { Ok(false) } else { // Loop using image1 for y in 0..image1.height { for x in 0..image1.width { // Get image1 pixel let pixel1 = image1.get_pixel(x, y)?; // Get image2 pixel let pixel2 = image2.get_pixel(x, y)?; // Compare pixel value if pixel1.r != pixel2.r || pixel1.g != pixel2.g || pixel1.b != pixel2.b { return Ok(false); } } } Ok(true) } } // Private functions // DifferenceHash // // Algorithm: // Reduce size. The fastest way to remove high frequencies and detail is to shrink the image. In // this case, shrink it to 9x8 so that there are 72 total pixels. Reduce color. Convert the image // to a grayscale picture. This changes the hash from 72 pixels to a total of 72 colors. Compute // the difference. The algorithm works on the difference between adjacent pixels. This identifies // the relative gradient direction. In this case, the 9 pixels per row yields 8 differences // between adjacent pixels. Eight rows of eight differences becomes 64 bits. Assign bits. Each bit // is simply set based on whether the left pixel is brighter than the right pixel. // // http://www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html // // fn diff_hash(image: &Image) -> RasterResult<Vec<u8>> { let width = 9; let height = 8; let mut image = image.clone(); // copy it since resize is desctructive editor::resize(&mut image, width, height, ResizeMode::Exact)?; // Resize to exactly 9x8 // Build hash let mut hash = Vec::new(); for y in 0..height { // Get the pixel value for the leftmost pixel. let pixel = image.get_pixel(0, y)?; let mut left = ((pixel.r as f32 + pixel.g as f32 + pixel.b as f32) / 3.0).floor(); // Get the pixel value for each pixel starting from position 1. for x in 1..width { let pixel = image.get_pixel(x, y)?; let right = ((pixel.r as f32 + pixel.g as f32 + pixel.b as f32) / 3.0).floor(); // Each hash bit is set based on whether the left pixel is brighter than the right // pixel. if left > right { hash.push(1); } else { hash.push(0); } // Prepare the next loop. left = right; } } Ok(hash) }