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
//! The module brings implementations of different image hashing algorithms.
use self::private::*;

use mat::CMat;
use *;

extern "C" {
    fn cv_hash_compute(phash: *const CHash, mat: *const CMat, result: *mut CMat);
    fn cv_hash_compare(phash: *const CHash, lhs: *const CMat, rhs: *mut CMat) -> f64;

    fn cv_average_hash_new() -> *mut CHash;
    fn cv_average_hash_drop(phash: *mut CHash);
    fn cv_block_mean_hash_new() -> *mut CHash;
    fn cv_block_mean_hash_drop(phash: *mut CHash);
    fn cv_color_moment_hash_new() -> *mut CHash;
    fn cv_color_moment_hash_drop(phash: *mut CHash);
    fn cv_marr_hildreth_hash_new() -> *mut CHash;
    fn cv_marr_hildreth_hash_drop(phash: *mut CHash);
    fn cv_phash_new() -> *mut CHash;
    fn cv_phash_drop(phash: *mut CHash);
    fn cv_radial_variance_hash_new() -> *mut CHash;
    fn cv_radial_variance_hash_drop(phash: *mut CHash);
}

mod private {
    #[allow(missing_copy_implementations, missing_debug_implementations)]
    pub enum CHash {}

    pub trait HashImpl {
        fn get_value(&self) -> *const CHash;
    }
}

#[allow(missing_docs)]
pub trait HashImplInterface: HashImpl {}

/// Basic trait for all hash types
pub trait Hash {
    /// Computes image hash
    fn compute(&self, mat: &Mat) -> Mat;

    /// Compares two image hashes
    fn compare(&self, lhs: &Mat, rhs: &Mat) -> f64;
}

impl<T: HashImplInterface> Hash for T {
    /// Computes image hash
    fn compute(&self, mat: &Mat) -> Mat {
        let result = CMat::new();
        let value = self.get_value();
        unsafe { cv_hash_compute(value, mat.inner, result) };
        Mat::from_raw(result)
    }

    /// Compares two image hashes
    fn compare(&self, lhs: &Mat, rhs: &Mat) -> f64 {
        let value = self.get_value();
        unsafe { cv_hash_compare(value, lhs.inner, rhs.inner) }
    }
}

macro_rules! impl_hash {
    ($x:ident, $ctor:ident, $drop:ident, $description:expr) => {
        #[doc=$description]
        #[derive(Debug)]
        pub struct $x {
            value: *const CHash,
        }

        impl $x {
            /// Creates new instance
            pub fn new() -> Self {
                let value = unsafe { $ctor() };
                Self { value }
            }
        }

        impl Drop for $x {
            fn drop(&mut self) {
                unsafe {
                    $drop(self.value as *mut _);
                }
            }
        }

        impl HashImpl for $x {
            fn get_value(&self) -> *const CHash {
                self.value
            }
        }

        impl HashImplInterface for $x {}

        // We know that this pointer is used for calling virtual pure functions,
        // But Rust doesn't allow us to share unsafe pointers between threads.
        // However, it's safe because the only place we mutate the pointer is `drop`,
        // Which makes the value inaccessible, so we're ok here too
        unsafe impl Send for $x {}
        unsafe impl Sync for $x {}
    };
}

impl_hash!(
    AverageHash,
    cv_average_hash_new,
    cv_average_hash_drop,
    "Computes average hash value of the input image"
);
impl_hash!(
    BlockMeanHash,
    cv_block_mean_hash_new,
    cv_block_mean_hash_drop,
    "Image hash based on block mean"
);
impl_hash!(
    ColorMomentHash,
    cv_color_moment_hash_new,
    cv_color_moment_hash_drop,
    "Image hash based on color moments"
);
impl_hash!(
    MarrHildrethHash,
    cv_marr_hildreth_hash_new,
    cv_marr_hildreth_hash_drop,
    "Marr-Hildreth Operator Based Hash, slowest but more discriminative."
);
impl_hash!(
    PHash,
    cv_phash_new,
    cv_phash_drop,
    "Slower than AverageHash, but tolerant of minor modifications"
);
impl_hash!(
    RadialVarianceHash,
    cv_radial_variance_hash_new,
    cv_radial_variance_hash_drop,
    "Image hash based on Radon transform"
);