diff --git a/benchmark.cpp b/benchmark.cpp index e179ae5..f3e745c 100644 --- a/benchmark.cpp +++ b/benchmark.cpp @@ -63,6 +63,17 @@ static void BM_ahash(benchmark::State &state) { free(buf); } +static void BM_mhash(benchmark::State &state) { + size_t size; + void *buf = load_test_file(&size); + + for (auto _ : state) { + mhash_mem(buf, tmp, size, state.range()); + } + + free(buf); +} + static void BM_multi(benchmark::State &state) { size_t size; void *buf = load_test_file(&size); @@ -83,6 +94,7 @@ BENCHMARK(BM_phash)->ArgName("size")->Arg(8); BENCHMARK(BM_whash)->ArgName("size")->Arg(8); BENCHMARK(BM_dhash)->ArgName("size")->Arg(8); BENCHMARK(BM_ahash)->ArgName("size")->Arg(8); +BENCHMARK(BM_mhash)->ArgName("size")->Arg(8); BENCHMARK(BM_multi)->ArgName("size")->Arg(8); diff --git a/fastimagehash.cpp b/fastimagehash.cpp index ecec515..cb61a35 100644 --- a/fastimagehash.cpp +++ b/fastimagehash.cpp @@ -31,6 +31,17 @@ double median(double *arr, size_t len) { } } +__always_inline +double median(uchar *arr, size_t len) { + std::sort(arr, arr + len); + + if (len % 2 == 0) { + return (double)(arr[(len / 2) - 1] + arr[len / 2]) / 2; + } else { + return arr[(len + 1 / 2)]; + } +} + void hash_to_hex_string(const uchar *h, char *out, int hash_size) { int hash_len = hash_size * hash_size / 4; @@ -121,6 +132,42 @@ int ahash_mem(void *buf, uchar *out, size_t buf_len, int hash_size) { return FASTIMAGEHASH_OK; } +int mhash_file(const char *filepath, uchar *out, int hash_size) { + size_t size; + void *buf = load_file_in_mem(filepath, &size); + + if (buf == nullptr) { + return FASTIMAGEHASH_READ_ERR; + } + + int ret = mhash_mem(buf, out, size, hash_size); + free(buf); + return ret; +} + +int mhash_mem(void *buf, uchar *out, size_t buf_len, int hash_size) { + Mat im; + try { + im = imdecode(Mat(1, buf_len, CV_8UC1, buf), IMREAD_GRAYSCALE); + resize(im, im, Size(hash_size, hash_size), 0, 0, INTER_AREA); + } catch (Exception &e) { + return FASTIMAGEHASH_DECODE_ERR; + } + + uchar *pixel = im.ptr(0); + const int endPixel = im.cols * im.rows; + + uchar sorted[im.cols * im.rows]; + memcpy(sorted, pixel, endPixel); + double med = median(sorted, endPixel); + + for (int i = 0; i < endPixel; i++) { + set_bit_at(out, i, pixel[i] > med); + } + + return FASTIMAGEHASH_OK; +} + int dhash_file(const char *filepath, uchar *out, int hash_size) { size_t size; void *buf = load_file_in_mem(filepath, &size); @@ -298,12 +345,13 @@ int phash_mem(void *buf, uchar *out, size_t buf_len, const int hash_size, int hi multi_hash_t *multi_hash_create(int hash_size) { auto multi_hash = (multi_hash_t *) malloc(sizeof(multi_hash_t)); - auto data = (uchar *) malloc((hash_size + 1) * 4); + auto data = (uchar *) malloc((hash_size + 1) * 5); multi_hash->ahash = data; multi_hash->phash = data + (hash_size + 1); multi_hash->dhash = data + (hash_size + 1) * 2; multi_hash->whash = data + (hash_size + 1) * 3; + multi_hash->mhash = data + (hash_size + 1) * 4; return multi_hash; } @@ -336,7 +384,7 @@ int multi_hash_mem(void *buf, multi_hash_t *out, size_t buf_len, return FASTIMAGEHASH_DECODE_ERR; } - Mat ahash_im; + Mat ahash_im; // Also used for mhash! Mat dhash_im; Mat phash_im; Mat whash_im; @@ -383,8 +431,15 @@ int multi_hash_mem(void *buf, multi_hash_t *out, size_t buf_len, uchar *pixel = ahash_im.ptr(0); int endPixel = ahash_im.cols * ahash_im.rows; + + // mhash + uchar mhash_sorted [ahash_im.cols * ahash_im.rows]; + mempcpy(mhash_sorted, pixel, endPixel); + double m_median = median(mhash_sorted, endPixel); + for (int i = 0; i < endPixel; i++) { set_bit_at(out->ahash, i, pixel[i] > avg); + set_bit_at(out->mhash, i, pixel[i] > m_median); } //dhash diff --git a/fastimagehash.h b/fastimagehash.h index 0e767d6..72bcb93 100644 --- a/fastimagehash.h +++ b/fastimagehash.h @@ -13,6 +13,7 @@ typedef struct multi_hash { uchar *phash; uchar *dhash; uchar *whash; + uchar *mhash; } multi_hash_t; #ifdef __cplusplus @@ -31,12 +32,10 @@ void hash_to_hex_string_reversed(const uchar *h, char *out, int hash_size); void hash_to_hex_string(const uchar *h, char *out, int hash_size); -/** - * - * @param buf - * @param buf_len - * @param hash_size - */ +int mhash_mem(void *buf, uchar *out, size_t buf_len, int hash_size); + +int mhash_file(const char *filepath, uchar *out, int hash_size); + int ahash_mem(void *buf, uchar *out, size_t buf_len, int hash_size); int ahash_file(const char *filepath, uchar *out, int hash_size); diff --git a/imhash.c b/imhash.c index 482bffd..c64b916 100644 --- a/imhash.c +++ b/imhash.c @@ -14,7 +14,7 @@ int main(int argc, char *argv[]) { return 0; } - int do_phash = 0, do_ahash = 0, do_whash = 0, do_dhash = 0; + int do_phash = 0, do_ahash = 0, do_whash = 0, do_dhash = 0, do_mhash = 0; for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--phash") == 0) { @@ -25,6 +25,8 @@ int main(int argc, char *argv[]) { do_whash = 1; } else if (strcmp(argv[i], "--dhash") == 0) { do_dhash = 1; + } else if (strcmp(argv[i], "--mhash") == 0) { + do_mhash = 1; } else { uchar hash[9]; @@ -54,6 +56,12 @@ int main(int argc, char *argv[]) { printf("%s\tw:%s\n", argv[i], hashstr); } } + if (do_mhash) { + if (mhash_file(argv[i], hash, 8) == 0) { + hash_to_hex_string_reversed(hash, hashstr, 8); + printf("%s\tm:%s\n", argv[i], hashstr); + } + } multi_hash_t *m = multi_hash_create(8); multi_hash_file(argv[i], m, 8, 4, 0); @@ -66,6 +74,8 @@ int main(int argc, char *argv[]) { printf("%s\tmd:%s\n", argv[i], hashstr); hash_to_hex_string_reversed(m->whash, hashstr, 8); printf("%s\tmw:%s\n", argv[i], hashstr); + hash_to_hex_string_reversed(m->mhash, hashstr, 8); + printf("%s\tmm:%s\n", argv[i], hashstr); multi_hash_destroy(m); }