The canonical way to get a perceptual hash is using the pHash library. In fact, we can get nearly the same performance in Haskell using hip and repa.
I ran these benchmarks on a six-core Linux machine using GHC's LLVM backend. Here is the criterion output:
benchmarking fileHash/cat.png
time 19.50 ms (20.88 ms .. 21.12 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 20.98 ms (20.92 ms .. 21.09 ms)
std dev 183.1 μs (98.64 μs .. 280.9 μs)
benchmarking foreignHash/cat.png
time 17.44 ms (17.25 ms .. 17.34 ms)
1.000 R² (1.000 R² .. 1.000 R²)
mean 17.28 ms (17.25 ms .. 17.30 ms)
std dev 53.08 μs (43.56 μs .. 74.98 μs)
foreignHash
is a wrapper for ph_dct_imagehash
from the pHash library;
as we can see, fileHash
nearly as fast.
You can find instructions for replicating the benchmark here.