作者:DMITRY PETROV
过去的几个周末,Kaggle的这个项目让我玩得乐此不疲Avito Duplicate Ads Detection problem。这个机器学习的问题,除了结构化的数据集之外,还包括超过1000万个图像。在这个项目的竞争中,许多玩家使用图像的哈希值而不是优化模型建立过程中的实际图像。
我发现有趣的是 – 最形象散列的实施采用了标准的离散余弦变换(CDT)。我曾经从事过多年的图像工作,我记得离散小波变换(DWT)可能会提供更好的结果的图像。我无法找到任何Python实现DWT基于图像的散列,所以我实现了一个并上传到imagehash库。这种变化在GitHub上的和新版本的包中实现。在本文中,我将简明扼要的介绍它是如何工作的。
1. Imagehash Python库
我发现的最简单有效的库是来自于约翰内斯布赫尔的imagehash库。可以在库中实现多种图像散列:aHash,pHash,dHash。所有三种方法的缩放图像转换成灰度级8×8的图像首位。然后该库执行用于每个64像素的一些计算并分配一个二进制的1或0的值。这些64bit值形成算法的输出。该bit计算方法是不同的:
aHash – 平均散列,对于每个像素输出1,如果该像素是大于或等于平均值,否则为0。
pHash – 感知哈希,不同于aHash,但首先它确实是离散余弦变换和频域的作品。
dHash – 梯度散列,计算每个像素的差值,并与平均差异的差异进行比较。
* wHash – 小波散列,几天前我把它添加到库里。它的工作原理在频域中作为pHash但它使用DWT代替DCT变换。
您可以查看本文的哈希值的更详细的说明。
下面这串代码展示了如何使用这个库
import PIL
from PIL import Image
import imagehash
hash1 = imagehash.phash(Image.open(‘test1.jpg’))
print(hash1)
hash2 = imagehash.phash(Image.open(‘test2.jpg’))
print(hash2)
hash1 == hash2
hash1 – hash2
从示例代码中的看出两个图像绝对不是相同的。64个bit中有42个是不同的。类似的图像差异将达到6-8bit。
2.计算图像的哈希值
对于普通的照片,基于频率一样pHash方法通常给出更好的结果,因为在频域对图像变换像更稳定:
JPG压缩
色彩模式更改或应用图像过滤器
大小缩放
甚至一些细微的图像编辑:裁剪图像的一部分,标志着由水印图像,添加修改图像的文本。
例如,让我们来看一看图像和相同图像的转换版本。这将是一个非常受欢迎的Lenna图像。许多图像处理研究使用这张图片。我还记得自我的学生时代起这幅画非常有名,约10年以前,当我做一些图像研究的时候。
Lenna.png. Original image. Size 512×512.
让我们在图像上的一些基本的转换和比较哈希值。首先,我们将推出尺寸变化从512×512到400×400像素。然后我们将改变颜色的模式,然后压缩为JPEG,作为最终步骤。
Lenna1.jpg. Color schema and image size were changed. JPG compressed. Size 400×400
import PIL
from PIL import Image
import imagehash
lenna = PIL.Image.open(‘lenna.png’)
lenna1 = PIL.Image.open(‘lenna1.jpg’)
h = imagehash.phash(lenna)
h1 = imagehash.phash(lenna1)
h-h1
哈…不错!即使经过压缩,调整大小和颜色变化的图像中没有差异哈希值。
让我们将更多的转换到lenna1.jpg图像(不是原单):
仅取画面的中央部
添加文字
再压缩
Lenna2.jpg. More image transformations. Size 317×360
我分享所有的三张图片:
lenna.png,lenna1.JPG,lenna2.jpg。
lenna2 = PIL.Image.open(‘lenna2.jpg’)
h2 = imagehash.phash(lenna2)
h – h2
(h – h2)/len(h.hash)**2
好吧。现在我们可以看到散差为20,即每哈希位31.2%。第二个指标是要好得多,因为散列大小的变化不同的哈希值。
aHash带来不同的结果。即使lenna1.jpg的简单改造显示1.6%的哈希差异。更明显的lenna2.jpg给出29.7%的差异。
a = imagehash.average_hash(lenna)
a1 = imagehash.average_hash(lenna1)
a2 = imagehash.average_hash(lenna2)
a – a1
(a – a1)/len(a.hash)**2
(a – a2)/len(a.hash)**2
3.小波哈希
离散小波变换(DWT)是频表示的另一种形式。流行的DCT和傅立叶变换使用余弦函数作为基础的一组罪过/:罪(x)时,罪(2×),罪(3倍),等等。与此相反,DWT使用一个单一的功能作为基础,但在不同的形式:缩放和移动。基础功能是可以改变的,这就是为什么我们可以有Haar小波,Daubechie-4小波等,这尺度效应给我们很大“时频表示”的时候,低频部分类似于原始信号。
这里有一个极好的为小波而备Python库 – pywt。我用这个库来实现的imagehash库whash()方法。默认情况下whash()计算用哈尔变换8×8的哈希值。另外,该方法删除最低哈尔频率LL(最大)。最低频率由来自仅一个数据点/像素,这点代表了图像的对比度,而不是对散列如此有用。
wHash Python代码如下:
w = imagehash.whash(lenna)
w1 = imagehash.whash(lenna1)
w2 = imagehash.whash(lenna2)
(w – w1)/len(w.hash)**2
(w – w2)/len(w.hash)**2
4.验证
为了使结果更清晰,让我们将图像与原始图像进行比较。预期散列差应为50%。这里是另一种比较标准图像 – barbara.jpg。让我们来计算使用所有哈希莱娜图和芭芭拉之间的哈希差异。代码如下:
Barbara.jpg
barb = PIL.Image.open(‘barbara.jpg’)
w_b = imagehash.whash(barb)
h_b = imagehash.phash(barb)
a_b = imagehash.average_hash(barb)
(a – a_b)/len(a.hash)**2
(h – h_b)/len(w.hash)**2
(w – w_b)/len(w.hash)**2
所有结果的列表:
aHash | pHash | wHash | |
lenna vs. lenna1 | 1.6% | 0% | 3.1% |
lenna vs. lenna2 | 29.7% | 31.3% | 28.1% |
lenna vs.barbara | 50% | 53.1% | 43.8% |
5.已知问题
当处理较大数目的小图像时我发现一个问题。pywt似乎有内存泄漏。这个问题已经github上提出了。我会尽力与pywt创作者联系有关问题。
通过与50K〜图像目录缓解这个问题我刚才分割图像的每个并重新运行处理为每个目录分别。
结论
很难说哪一种方法提供了更好的结果。这取决于你的应用程序,你应该专注于般精准/召回或AUC您的应用程序或机器学习模型度量。对于我Kaggle得分whash()+带来了0.04%的AUC指标,除了我目前的〜92.9%的结果。
它看起来并不是一种巨大的差异。但是,我们应该记住,在建模代码中,我们通过一个字母的变化来自phash()来实现这whash()。我很高兴能有更多的先进的分析工具,我希望这种方法将是一个很好的补充您的分析工具箱。此外,我认为,wHash具有由方法参数进行调优的巨大潜力。
请分享你在使用本库的经验、任何意见、建议、代码改进和修正,当然还有赞赏。
原文链接:https://fullstackml.com/2016/07/02/wavelet-image-hash-in-python/