PIL图像的简单哈希

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PIL图像的简单哈希相关的知识,希望对你有一定的参考价值。

背景

我想将PIL图像的信息存储在键值存储中。为此,我散列图像并使用哈希作为键。

我尝试了什么

我一直在使用以下代码来计算哈希值:

def hash(img):
   return hashlib.md5(img.tobytes()).hexdigest()

但似乎这不稳定。我还没弄清楚为什么,但是对于不同机器上的相同图像,我会得到不同的哈希值。

是否有一种简单的散列图像方式,只取决于图像本身(而不是时间戳,系统架构等)?

请注意,我不需要类似的图像来获得类似/相同的哈希,就像在image hashing中一样。实际上,我希望不同的图像具有不同的散列,例如改变图像的亮度应改变其散列。

答案

我猜你的目标是在Python中执行图像散列(这与经典散列有很大的不同,因为图像的字节表示取决于格式,分辨率等)

其中一种图像散列技术是平均散列。确保这不是100%准确,但在大多数情况下它可以正常工作。


首先,我们通过减小图像的尺寸和颜色来简化图像,降低图像的复杂性,大大有助于提高其他图像之间的比较精度:

缩小尺寸:

img = img.resize((10, 10), Image.ANTIALIAS)

减少颜色:

img = img.convert("L")

然后,我们找到图像的平均像素值(这显然是平均散列的主要组成部分之一):

pixel_data = list(img.getdata())
avg_pixel = sum(pixel_data)/len(pixel_data)

最后计算哈希值,我们将图像中的每个像素与平均像素值进行比较。如果像素大于或等于平均像素,那么我们得到1,否则它是0.然后我们将这些位转换为基数16表示:

bits = "".join(['1' if (px >= avg_pixel) else '0' for px in pixel_data])
hex_representation = str(hex(int(bits, 2)))[2:][::-1].upper()

如果要将此图像与其他图像进行比较,则执行上述操作,并找出平均散列图像的十六进制表示之间的相似性。您可以使用像hamming distance这样简单的东西或更复杂的算法,如Levenshtein distanceRatcliff/Obershelp pattern recognition(SequenceMatcher),Cosine Similarity等。

另一答案

认识到你对时间戳的看法,ImageMagick就是这样一个功能。首先,一个例子。

在这里,我创建两个具有相同像素的图像,但时间戳至少为1秒不同:

convert -size 600x100 gradient:magenta-cyan 1.png
sleep 2
convert -size 600x100 gradient:magenta-cyan 2.png

enter image description here

如果我在macOS上对它们进行校验,它会告诉我它们因嵌入的时间戳而不同:

md5 -r [12].png

c7454aa225e3e368abeb5290b1d7a080 1.png
66cb4de0b315505de528fb338779d983 2.png

但是,如果我使用ImageMagick校验像素,(其中%#是像素校验和),它知道像素是相同的,我得到:

identify -format '%# - %f
' 1.png 2.png
70680e2827ad671f3732c0e1c2e1d33acb957bc0d9e3a43094783b4049225ea5 - 1.png
70680e2827ad671f3732c0e1c2e1d33acb957bc0d9e3a43094783b4049225ea5 - 2.png

事实上,如果我使用相同的图像内容制作TIFF文件,无论是使用Motorola还是Intel字节顺序,还是使用NetPBM PPM文件:

convert -size 600x100 gradient:magenta-cyan -define tiff:endian=msb 3motorola.tif
convert -size 600x100 gradient:magenta-cyan -define tiff:endian=lsb 3intel.tif
convert -size 600x100 gradient:magenta-cyan 3.ppm

尽管文件格式,CPU架构和时间戳不同,ImageMagick知道它们是相同的,但是:

identify -format '%# - %f
' 1.png 3.ppm 3{motorola,intel}.tif

70680e2827ad671f3732c0e1c2e1d33acb957bc0d9e3a43094783b4049225ea5 - 1.png
70680e2827ad671f3732c0e1c2e1d33acb957bc0d9e3a43094783b4049225ea5 - 3.ppm
70680e2827ad671f3732c0e1c2e1d33acb957bc0d9e3a43094783b4049225ea5 - 3motorola.tif
70680e2827ad671f3732c0e1c2e1d33acb957bc0d9e3a43094783b4049225ea5 - 3intel.tif

所以,在回答你的问题时,我建议你使用Python子进程模块向ImageMagick发送shell并使用ImageMagick。

以上是关于PIL图像的简单哈希的主要内容,如果未能解决你的问题,请参考以下文章

python PIL 简单图像处理

PIL打开图像的numpy数组中的元素数量错误

在 Tkinter 中调整 PIL 中的图片大小

python 三种方式实现截屏(详解+完整代码)

python----图像简单处理(PIL or Pillow)

如何在Python中将图像分割成多个片段