图像指纹用于比较多张图片的相似度

99

我需要创建大量图像的指纹(大约有100,000张现有图像,每天新增1,000张,RGB,JPEG,最大尺寸为800x800),以便快速比较每个图像与其他每个图像。我不能使用二进制比较方法,因为还应识别几乎相似的图像。

最好是已有的库,但对现有算法的一些提示也会帮助我很多。


1
应该为哪种编程语言编写这个库? - Ben S
11个回答

60

普通的哈希或CRC算法对图像数据处理效果不佳,必须考虑到信息的维度特性。

如果需要非常强大的指纹识别,使仿射变换(缩放、旋转、平移、翻转)被考虑在内,则可以使用Radon变换来生成图像源的规范映射并将其与每个图像一起存储,然后比较仅指纹。这是一个复杂的算法,不适合心脏虚弱者。

有几种简单的解决方案:

  1. 为图像创建亮度直方图作为指纹
  2. 创建每个图像的缩小版本作为指纹
  3. 将技术(1)和(2)结合成混合方法以改善比较质量

亮度直方图(特别是分隔为RGB组件的直方图)是图像的一个合理指纹,可以相当高效地实现。从另一个直方图中减去一个直方图将产生一个新的直方图,您可以处理它来决定两个图像有多相似。直方图因仅评估亮度/颜色信息的分布和出现而很好地处理仿射变换。如果将每个颜色组件的亮度信息量化为8位值,则768字节的存储空间足以存储几乎任何合理大小的图像的指纹。但是,在图像的颜色信息被操纵时,亮度直方图会产生误报。如果应用对比度/亮度、海报化、颜色转移等变换,则亮度信息将发生变化。某些类型的图像,如风景和单色占支配地位的图像,也可能发生误报。

使用缩放图像是另一种减少图像信息密度以便更容易比较的方法。将原始图像尺寸缩小到不低于其 10% 的大小普遍会削减掉太多信息而无法使用——因此,一个 800x800 像素的图像可以被缩小到 80x80 并仍然提供足够的信息来进行良好的指纹识别。与直方图数据不同,当源分辨率具有不同的长宽比时,您必须对图像数据进行各向异性缩放。换言之,将 300x800 图像缩小为 80x80 缩略图会导致图像变形,当与非常相似的 300x500 图像进行比较时,会产生误判。如果涉及仿射变换,缩略图指纹也经常会产生误判。如果翻转或旋转图像,它的缩略图与原始图像会有很大的区别,可能会导致误判。
将两种技术结合起来是一种合理的方式,可以降低假阳性和假阴性的发生率。

关于CRC,同意。然而,如果想要使用它,最好使用MD5哈希而不是CRC32。 - mloskot
6
你不希望使用MD5,因为它是一种单向的密码哈希算法。你需要使用一个哈希算法,它可以为相似的输入产生类似的结果,以便你可以直接比较哈希之间的差异。 - AJ Quick
我认为这被称为局部敏感哈希 - xdhmoore

34

这里提出了一种比之前提出的缩小图像变体更为系统的方法,保留了它们的一般特点,但在所进行的操作上提供了更严谨的数学基础。

首先对图像进行Haar小波变换。简单来说,Haar小波是从低分辨率图像到每个高分辨率图像的差异继承的一系列权重,但权重取决于你在mipmap '树'中的深度。计算非常直观。然后,在适当加权 Haar 小波之后,保留仅有前k个最大系数(按绝对值),并进行向量归一化和保存。

如果您使用这些已经归一化的向量之间的点积,则可以得到相似性量度,其中1表示几乎相同。我在这里发布了更多信息。


21

一定要看看phash

关于图像比较有这个php项目: https://github.com/kennethrapp/phasher

还有我的小型javascript克隆版: https://redaktor.me/phasher/demo_js/index.html

不幸的是这基于“位计数”,但会识别旋转的图像。另外一种javascript的方法是通过canvas从图像中构建亮度直方图。可以在画布上可视化多边形直方图,然后将其与数据库(例如mySQL spatial…)中的多边形进行比较。


这个在npm上有吗?我正在寻找一种使用JavaScript比较两张图片相似度的方法。 - chovy
嗯,我以为这对npm来说太便宜了。实际上,这只是一个快速从头编写的演示。但是,请随意使用源代码。如果我能做到的话,我稍后会查看它并将其推送到github https://github.com/redaktor/... - sebilasse
@SebastianLasse,我刚刚看了你的JS端口,太棒了!只是我希望可以将图像URI传递给Compare()函数,而不是必须先下载图像。另外,根据我的测试,“非常相似图像”的阈值应该是> 90%,而不是> 98%。 - thdoan

12
很久以前,我曾经参与开发一个拥有类似特征的系统,下面是我们采用的算法:

  1. 将图片分成多个区域。在我们的情况下,处理的是 4:3 分辨率的视频,因此我们使用了12个区域。这样可以消除源图像分辨率的影响。
  2. 计算每个区域的整体颜色——即该区域内所有像素的平均颜色。
  3. 计算整张图片的整体颜色——即所有区域的平均颜色。

因此,对于每个图像,您需要存储 n+1 个整数值,其中 n 是您要跟踪的区域数量。

为了进行比较,您还需要单独查看每个颜色通道。

  1. 对于整张图片,比较整体颜色的每个颜色通道,以查看它们是否在一定阈值范围内——例如,10%。
  2. 如果图像在阈值范围内,则比较每个区域。如果所有区域也都在阈值范围内,则图像足够匹配,您至少可以标记它们进行进一步的比较。

这样可以快速筛选不匹配的图像;您还可以使用更多区域和/或递归应用算法以获取更强的匹配置信度。


6
类似于Ic的回答-您可以尝试在多个分辨率下比较图像。因此,每个图像都保存为1x1、2x2、4x4..800x800。如果最低分辨率不匹配(受阈值限制),则可以立即拒绝它。如果它匹配,则可以在下一个更高的分辨率下进行比较,依此类推。另外,如果这些图像共享任何相似的结构,例如医学图像,您可能能够将该结构提取到一个更容易/更快速比较的描述中。

这似乎对应于某种树搜索。很有趣。 - André Laszlo

3

截至2015年(回到未来...在谷歌上高排名的2009年问题),可以使用深度学习技术计算图像相似性。被称为自动编码器的算法族可以创建可搜索相似性的向量表示。这里有一个演示链接


能否从二进制数据生成指纹图像? - SwR
当然,有适用于此任务的人工神经网络,但您的回答似乎并没有真正回答任何问题。问题是:这是如何完成的?链接页面没有披露任何信息,“自动编码器”这个术语也没有帮助。 - Simon Steinberger
原始问题并没有说“怎么做?”,但它确实说“一些现有算法的提示会对我很有帮助”,这就是我提供的内容。 - Alex R
你没有给算法链接一个"提示",事实上链接的页面上说:"它能工作,但没有人知道为什么。请不要对结果抱有太大期望"。 - odyth
这个网址 https://deeplearning4j.org/deepautoencoder#use-cases 更清晰地说明了如何使用自编码器创建指纹,以及如何根据顶点的相似程度在其他图像中找到相似之处。 - odyth
很容易比较两张图片以确定它们是否大致相同。但是如果图像大致相同,那么如何确定图像的指纹也大致相同呢? - user1631977

2

2
所以你想进行“指纹匹配”,这与“图像匹配”有很大不同。在过去的20年中,指纹分析已经得到深入研究,并且已经开发出了几种有趣的算法来确保正确的检测率(相对于FARFRR度量-误接受率误拒绝率)。
我建议您更好地了解基于细节检查构建的检测技术的LFA(局部特征分析)类别。细节是任何指纹的特定特征,并已分类为几类。将光栅图像映射到细节图是实际上大多数公共机构用于归档罪犯或恐怖分子的方法。
请参见此处以获取进一步的参考资料。

如果你有一个给定生物识别系统的得分高斯分布,你知道如何计算假接受率吗? - GobiasKoffi
2
OP 想要 "创建许多图像的指纹"。不是比较人类指纹的图像。 - Navin

2
一种方法是缩小图像的尺寸并显著降低分辨率(到200x200?), 存储一个更小(像素平均)的版本来进行比较。然后定义一个容差阈值并比较每个像素。如果所有像素的RGB都在容差范围内,就匹配了。 初始运行时间为O(n^2),但如果分类所有匹配项,每个新图像只需要O(n)算法进行比较(只需将它与已插入的每个图像进行比较)。然而,随着要比较的图像列表变得越来越大,它最终会崩溃,但我认为你还是可以安全地使用一段时间。
运行400天后,您将有500,000张图像,这意味着(不计算缩小图像的时间) 200(H) * 200(W) * 500,000(图像) * 3(RGB) = 60,000,000,000次比较。如果每个图像都完全匹配,你就会落后,但这可能不是情况,对吧? 记住,只要单个比较超出了您的阈值,您就可以将图像视为不匹配。

2

您是否真的想将每个图像与其他图像进行比较?这个应用程序是什么?也许您只需要某种基于特定描述符的图像索引和检索?例如,您可以查看多媒体内容描述界面的MPEG-7标准。然后,您可以比较不同的图像描述符,这可能不太精确,但速度更快。


也许是全面和有限之间的选择。 - johnny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接