图像比较 - 快速算法

时间:2009-05-09 20:18:18

标签: image algorithm comparison computer-vision

我正在寻找创建图像基础表,然后将任何新图像与其进行比较,以确定新图像是否是基础的精确(或接近)副本。

例如:如果您想减少100次相同图像的存储空间,您可以存储它的一个副本并提供它的参考链接。输入新图像时,您想要与现有图像进行比较,以确保它不是重复的...想法?

我的一个想法是缩小为一个小缩略图,然后随机选择100个像素位置并进行比较。

10 个答案:

答案 0 :(得分:433)

答案 1 :(得分:78)

我所知道的最好的方法是使用Perceptual Hash。似乎有一个很好的开源实现这样的哈希可用于:

http://phash.org/

主要思想是通过识别原始图像文件中的显着特征并散列这些特征的紧凑表示(而不是直接散列图​​像数据)来将每个图像缩小为小的哈希码或“指纹”。这意味着通过简单的方法可以大大降低误报率,例如将图像缩小到一个微小的指纹大小的图像并比较指纹。

phash提供了几种类型的哈希,可用于图像,音频或视频。

答案 2 :(得分:33)

这篇文章是我的解决方案的起点,这里有很多好点子,所以我会分享我的结果。主要的见解是我找到了一种方法,通过利用phash的速度来解决基于关键点的图像匹配的缓慢。

对于一般解决方案,最好采用多种策略。每种算法最适合某些类型的图像转换,您可以利用它。

在顶部,最快的算法;在底部最慢(虽然更准确)。如果在更快的级别找到一个好的匹配,你可以跳过慢的。

  • 基于文件哈希(md5,sha1等)的完全重复
  • 重新缩放图像的感知哈希(phash)
  • 基于特征的(SIFT)修改图像

我用phash获得了非常好的结果。重新缩放图像的准确性很好。对于(感知)修改的图像(裁剪,旋转,镜像等)并不好。为了处理散列速度,我们必须使用磁盘缓存/数据库来维护大海捞针的哈希值。

关于phash的一个非常好的事情是,一旦你构建你的哈希数据库(对我来说大约是1000个图像/秒),搜索可以非常非常快,特别是当你可以将整个哈希数据库保存在内存中时。这是相当实用的,因为哈希只有8个字节。

例如,如果您有100万个图像,则需要一个包含100万个64位哈希值(8 MB)的数组。在某些CPU上,这适合L2 / L3缓存!在实际应用中,我看到核心数比较超过1千兆赫/秒,这只是CPU内存带宽的问题。一个10亿像素的数据库在64位CPU上是可行的(需要8GB RAM),搜索时间不会超过1秒!

对于修改/裁剪的图像,它似乎是一种变换不变的特征/关键点检测器,如SIFT是可行的方法。 SIFT将生成检测裁剪/旋转/镜像等的良好关键点。然而,与phash使用的汉明距离相比,描述符比较非常慢。这是一个主要限制。有很多比较要做,因为有最大的IxJxK描述符比较查找一个图像(I = num haystack图像,J =每个干草堆图像的目标关键点,K =每个针图像的目标关键点)。

为了解决速度问题,我尝试在每个找到的关键点周围使用phash,使用特征尺寸/半径来确定子矩形。使这项工作顺利进行的技巧是增大/缩小半径以生成不同的子矩形水平(在针图像上)。通常情况下,第一级(未缩放)将匹配,但通常需要更多。我不是100%确定为什么会这样,但我可以想象它可以使功能太小而不能使用phash(phash将图像缩放到32x32)。

另一个问题是SIFT不会以最佳方式分发关键点。如果图像的一部分有很多边缘,那么关键点将聚集在那里,你不会在另一个区域得到任何一个。我在OpenCV中使用GridAdaptedFeatureDetector来改进分发。不确定网格尺寸最好,我使用的是小网格(1x3或3x1,具体取决于图像方向)。

您可能希望在特征检测之前将所有干草堆图像(和针)缩放到较小的尺寸(我沿最大尺寸使用210px)。这将减少图像中的噪声(对于计算机视觉算法来说总是一个问题),也会将探测器集中在更突出的特征上。

对于人物的图像,您可以尝试使用面部检测并使用它来确定要缩放到的图像大小和网格大小(例如,最大面部缩放为100px)。特征检测器考虑了多个比例级别(使用金字塔)但是它将使用多少级别(当然这是可调的)。

当关键点检测器的返回量小于您想要的功能数量时,它可能效果最佳。例如,如果你要求400并获得300,这很好。如果你每次都回来400,那么可能不得不遗漏一些好的功能。

针图像可以比干草堆图像具有更少的关键点,并且仍然获得良好的结果。添加更多并不一定会带来巨大的收益,例如J = 400和K = 40,我的命中率约为92%。当J = 400且K = 400时,命中率仅上升至96%。

我们可以利用汉明函数的极速来解决缩放,旋转,镜像等问题。可以使用多次通过技术。在每次迭代时,转换子矩形,重新哈希,然后再次运行搜索功能。

答案 3 :(得分:6)

正如cartman指出的那样,您可以使用任何类型的哈希值来查找完全重复的内容。

找到近距离图像的一个起点可能是here。这是CG公司用来检查修改后的图像是否仍然显示相同场景的工具。

答案 4 :(得分:6)

我有一个想法,它可以工作,而且很可能非常快。 您可以对图像进行二次采样,分辨率为80x60或类似, 并将其转换为灰度(在二次采样后它会更快)。 处理您想要比较的两个图像。 然后运行两个图像(查询图像和每个图像之间)的平方差的归一化和, 甚至更好的归一化交叉相关,如果,则响应接近1 两个图像都很相似。 然后,如果图像相似,您可以继续使用更复杂的技术 验证它是相同的图像。 显然,该算法在数据库中的图像数量方面是线性的 所以尽管在现代硬件上每秒可以达到10000张图像。 如果需要旋转不变,则可以计算主导梯度 对于这个小图像,然后整个坐标系可以旋转到规范 方向,这虽然会慢一些。不,这里没有不变的规模。

如果你想要更通用的东西或使用大型数据库(数百万张图片),那么 你需要研究图像检索理论(过去5年出现的论文)。 其他答案中有一些指示。但它可能是矫枉过正,建议直方图方法将完成这项工作。虽然我会想到许多不同的组合 快速的方法会更好。

答案 5 :(得分:5)

我相信将图像的大小降低到几乎图标大小,比如48x48,然后转换为灰度,然后取像素或Delta之间的差异应该可以正常工作。因为我们正在比较像素颜色的变化而不是实际的像素颜色,所以如果图像稍微更亮或更暗则无关紧要。大的变化将很重要,因为像素变得太亮/暗将会丢失。您可以将这个应用于一行,也可以根据需要应用,以提高准确性。为了形成一个类似的密钥,你最多需要47x47 = 2,209个减法。

答案 6 :(得分:3)

挑选100个随机点可能意味着相似(或偶尔甚至不相似)的图像会被标记为相同,我认为这不是您想要的。如果图像是不同的格式(png,jpeg等),具有不同的大小或具有不同的元数据,MD5哈希将不起作用。将所有图像缩小到更小的尺寸是一个不错的选择,只要您使用好的图像库/快速语言并且尺寸足够小,进行像素对像素比较不应该花费太长时间。

你可以尝试将它们变得很小,如果它们是相同的,那么在较大尺寸上进行另一次比较 - 可以是速度和准确性的良好组合......

答案 7 :(得分:2)

如果您有大量图像,请查看Bloom filter,它使用多个哈希值来获得可能但效果很好的结果。如果图像数量不是很大,那么像md5这样的加密哈希就足够了。

答案 8 :(得分:1)

我们松散地称为重复的东西可能很难被算法识别出来。 您的重复项可以是:

  1. 完全重复
  2. 近乎精确的重复。 (图像等的小编辑)
  3. 感知重复(相同的内容,但不同的视图、相机等)

No1 和 2 更容易解决。否 3. 非常主观,仍然是一个研究课题。 我可以为 No1 & 2 提供解决方案。 两种解决方案都使用了优秀的图像哈希库:https://github.com/JohannesBuchner/imagehash

  1. 完全重复 可以使用感知散列度量找到精确的重复项。 phash 库在这方面做得很好。我经常用它来清洁 训练数据。 用法(来自 github 站点)非常简单:
from PIL import Image
import imagehash

# image_fns : List of training image files
img_hashes = {}

for img_fn in sorted(image_fns):
    hash = imagehash.average_hash(Image.open(image_fn))
    if hash in img_hashes:
        print( '{} duplicate of {}'.format(image_fn, img_hashes[hash]) )
    else:
        img_hashes[hash] = image_fn
  1. 近乎精确的重复 在这种情况下,您必须设置一个阈值并比较散列值与每个散列值的距离 其他。这必须通过对图片内容的反复试验来完成。
from PIL import Image
import imagehash

# image_fns : List of training image files
img_hashes = {}
epsilon = 50

for img_fn1, img_fn2 in zip(image_fns, image_fns[::-1]):
    if image_fn1 == image_fn2:
        continue

    hash1 = imagehash.average_hash(Image.open(image_fn1))
    hash2 = imagehash.average_hash(Image.open(image_fn2))
    if hash1 - hash2 < epsilon:
        print( '{} is near duplicate of {}'.format(image_fn1, image_fn2) )

答案 9 :(得分:0)

我的公司每个月大约有 2400万张来自制造商的图片。我一直在寻找一种快速的解决方案,以确保我们上传到目录中的图像是 new 图像。

我想说的是,我已经在互联网上进行了广泛搜索,试图找到理想的解决方案。我什至开发了自己的边缘检测算法。
我评估了多个模型的速度和准确性。 我的图像具有白色背景,在进行相位调整时效果非常好。就像 redcalx 所说,我建议使用phash或ahash。 请勿使用MD5哈希或其他任何加密哈希。除非您只希望完全匹配图像。图像之间发生的任何调整大小或操作都会产生不同的哈希值。

对于phash / ahash,请检查以下内容:imagehash

我想通过发布我的代码和我的准确性来扩展* redcalx'*的帖子。

我做什么:

from PIL import Image
from PIL import ImageFilter
import imagehash

img1=Image.open(r"C:\yourlocation")
img2=Image.open(r"C:\yourlocation")
if img1.width<img2.width:
    img2=img2.resize((img1.width,img1.height))
else:
    img1=img1.resize((img2.width,img2.height))
img1=img1.filter(ImageFilter.BoxBlur(radius=3))
img2=img2.filter(ImageFilter.BoxBlur(radius=3))
phashvalue=imagehash.phash(img1)-imagehash.phash(img2)
ahashvalue=imagehash.average_hash(img1)-imagehash.average_hash(img2)
totalaccuracy=phashvalue+ahashvalue

这是我的一些结果:

item1  item2  totalaccuracy
desk1  desk2       3
desk2  phone1     22
chair1 desk1      17
phone1 chair1     34

其中项目代表图像的实际主题,数字代表方向的比例。

希望这会有所帮助!

相关问题