【问题标题】:Compare 2 One Bit Images for Similarity比较 2 个一位图像的相似性
【发布时间】:2016-03-21 16:43:58
【问题描述】:

我正在尝试将源图像与一组中的数千张图像进行比较,以获得最可能匹配的相似度分数 (0 - 1)。每个图像都很小(64x64 或更小)。每个图像都是 1 位,这意味着每个像素要么关闭(完全透明),要么打开(全白)。我正在尝试创建一个非常快速的相似性算法来比较这些图像。我通过 Google 搜索找到了许多相似性算法,但它们都涉及比较大型全彩色图像,我不需要这样做。

我意识到我可以只比较匹配/不匹配的像素,但这可能会很慢,因为比较集可能非常大。比较集图像的大小都将与查找图像完全相同。

是否可以为这些类型的图像创建哈希或其他快速查找,其中可以执行哈希或二进制搜索查找并使用最可能的匹配创建相似度分数?

【问题讨论】:

  • 您是否必须比较许多图像以获得独特的图像,还是仅比较 2 个图像?
  • @Haris 比较集中会有很多图片
  • 指定“比较”的含义:检查两个图像是否完全相同(因此您得到一个布尔结果),或检查两个图像的相似程度(因此您得到一个分数)。此外,如果您正在考虑轮换。
  • @ChronoTrigger 问题已更新,我正在尝试计算相似度分数
  • “数千”个 32x32 的 1 位像素图像实际上并没有多少数据。逐像素计算有多“慢”?您需要什么级别的性能?您是否一直将相同的集合与新的集合进行比较,或者这是一次性的,即预先计算会有所帮助吗?

标签: image algorithm


【解决方案1】:

要获得二进制图像的比较分数,我建议您使用 xor 运算计算 Hamming distance,然后计算个数。使用 SSSE3 指令的快速 popcount 操作可以大大加快这一速度。

汉明距离告诉您两个二进制字符串之间不同的位数(因此它实际上是一个相异值)。要获得该范围内的分数,例如,[0, 1],您可以除以图像的大小(这样您就可以获得与图像大小无关的分数)。

关于上千张图片的对比,请确保它是一个瓶颈,因为如果数据不是那么大,它可能会比你想象的要快。如果您仍需要加快速度,可以考虑以下任何一种或两种想法:

1) 并行化:例如,该函数可能很容易与 OpenMP 或 tbb 并行化。 2)哈希表:使用每个图像的第一个(或一些子集)位在向量中索引它们。然后,仅比较属于同一哈希箱的那些图像。当然,这是一种近似的方法,你不会得到任何一对图像的比较分数,只有那些足够相似的。

请记住,如果要与所有图像进行比较,则必须对所有数据库运行完整的比较,因此除了并行化之外几乎没有机会加快速度。

【讨论】:

  • 这似乎是最快的方法,但仍需要针对每个源图像的整个查找集进行迭代。不过我会尝试一下,看看性能如何。
  • 我添加了一些想法以使其更快。
  • 这似乎运作良好。结合@m69 的优化,它的运行速度比我想象的要快得多。我在游戏中使用它进行实时图像匹配。用户绘制实体的图片,游戏创建他们绘制的内容。有点像 Scribblenauts,但用图片而不是文字 :)
【解决方案2】:

一种方法是使用二叉树。每个图像的像素都可以转换为一串 1 和 0。那么这个字符串就可以用来构造一棵二叉树了。

在检查新字符串时,您只需开始跟随路径将您带到的位置,如果您到达叶节点,则它存在,如果您不存在,则它是新的。

上图显示了使用 3 个长度为 4 的字符串构造的树

1010
0110
0001

所以,如果0001 再次出现,只需按照路径走,如果您最终出现在叶子(实心圆圈)中,则字符串(图像)是重复的并且再次出现。如果没有,那么您也可以添加它,同时知道它是新的和独特的。

每次比较和加法都需要0(n) 时间,其中n 是字符串的长度。在你的情况下n == 32*32

【讨论】:

  • 感谢您的回答。我希望计算相似度分数,这意味着还应该考虑几乎重复的图像。这种方法可以处理吗?
  • @jjxtra 您可以使用正确匹配路径的字符串长度,然后计算百分比。这可以作为相似度得分。
  • @Haris 不幸的是,这只会比较第一个像素不匹配的点,这意味着除了第一个像素之外相同的两个图像将被认为比两个图像不太相似,其中除了前两个像素之外,它们完全不同,这不是一个很好的比较算法
【解决方案3】:

你可以实现一个四叉树结构https://en.wikipedia.org/wiki/Quadtree

递归分割您的图像。在每个级别,存储 1 和/或 0 像素的数量(一个可以从另一个计算得出)

例如:对于这张图片:

0 1 1 0

0 1 0 1

0 0 0 0

0 0 1 0

您计算以下树:

(5)

(2) - (2) - (0) - (1)

(0) - (1) - (0) - (1) - - - (1) - (0) - (0) - (1) - - - (0) - (0) - (0) - (0) - - - (0) - (0) - (1) - (0)

树的更高级别是图像的更粗略版本:

第一级:

5/16

二级:

2/4 2/4

0/4 1/4

然后,您的相似度分数可能是计算 0 和 1 的数量是否不同,在不同的递归级别,每个级别的权重。而且你可以通过不走整棵树来获得它的近似值(以快速消除非常不同的图像)。

【讨论】:

    【解决方案4】:

    如果您发现完全比较所有图像(使用例如 ChronoTrigger 的答案)仍然需要太多时间,请考虑这两种策略以减少必要比较的次数。

    我会假设图像是逐行比较的。您首先完全比较第一张图像,将其分数存储为最大值,然后继续下一张,每次都根据需要更新最大值。在逐行比较每个图像时,在每行之后(或每 n 行之后)执行以下操作:

    1. 检查到目前为止的不匹配位数是否超过图像中迄今为止得分最高的不匹配数。如果是这样,则此图像永远无法达到最高分,并且可以丢弃。
    2. 如果目前每行的平均得分低于得分最高的图像的每行平均得分,则在下次运行时继续进行比较,然后跳到下一张图像。

    重复此操作,直到所有图像都被完全检查或丢弃。

    在基于示例图像的 100 个随机 32x32 像素图像上尝试此策略,每个图像都更改了随机位数,得到了这个有希望的结果:

    首次运行(100 张图片): 完全检查的图像:5(最高分数:1015,图像#52) 推迟 1 行后:59 1 行后丢弃:35 10 行后丢弃:1 第二次运行(59 幅图像): 没有额外检查就被丢弃:31(因为新的最高分数) 在增加 1 行后丢弃:12 额外 2 行后丢弃:9 在另外 3 行后丢弃:1 附加 4 行后丢弃:3 额外 5 行后丢弃:1 在 6 个附加行后丢弃:2 比较的总行数:3200 行中的 326 行(~ 100 图像中的 10.1875)

    【讨论】:

    • 我今天会试试这个。看起来很有希望。
    • @jjxtra 有用性很大程度上取决于最佳图片与原始图片的接近程度;如果它只有少数不正确的像素,其他图片可以很快被丢弃。您可以通过在第一次运行中始终检查至少 2、4、... 行来微调算法,始终检查 2、4、8、... 组中的行,仅当每行得分低于时才推迟比较90%, 75%, ... 每行得分的最大值,一旦超过一定数量的行,总是检查整个图像,一旦找到非常高的最大值就停止推迟,...
    • 到目前为止,这种方法的性能非常好。在不迭代所有这些额外行的情况下尽早丢弃是一个很大的速度提升。
    【解决方案5】:

    如果你的图片以类似位图的格式存储像素数据,那么每一行只是32位整数值,你可以简单地比较图片行

    for iy = 0 to ImageHeight - 1 do
      if   CastToInt32(Image1.Scanline[0]) <> CastToInt32(Image2.Scanline[0]) then 
         break due to inequality
    //32 comparisons or less
    

    对于近似相似的情况,您可以计算所有行的异或值中的集合位的差异总数。

    NumberOf1Bits(Value1 xor Value2)

    附: Delphi 中的简单实现每一张图像/图像比较需要 300 纳秒(100 万张图像需要 0.3 秒)。单线程,i5 处理器,不匹配限制 450。 低失配限制的时间将大大缩短(限制 45 为 47 ns)。 主要时间吃者 - NumberOf1Bits/popcount 函数。

    【讨论】:

    • 这适用于精确比较,但我需要一个相似度分数,因此还必须使用某种阈值计算接近精确的图像。此外,我希望有一种快速的方法来比较成百上千张图片。
    • @jjxtra 添加了基于异或的方法
    • 只要 jjxtra 不想比较可以翻译或旋转的图像,基于 xor 的方法可能是最好的答案
    • @KevinWells 没错。我将对源图像和查找集进行预处理,以使它们具有相同的大小。
    • @m69 是的,当不匹配的位数超过限制时,我使用了投掷图像。增益取决于限值(最多 20 倍)
    【解决方案6】:

    我为 Skia4Delphi 库单元测试创​​建了一个图像哈希类。它生成一个散列,可以仅使用散列来比较 2 个图像之间的相似度百分比。重点是准确性而不是性能,但性能还不错。要使用它,您必须安装 Skia4Delphi。查看来源:https://github.com/skia4delphi/skia4delphi/blob/main/Tests/Source/Skia.Tests.Foundation.ImageHash.pas

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-02
      • 2011-08-09
      • 1970-01-01
      • 2018-02-06
      • 2015-10-22
      • 2021-08-25
      • 2012-01-23
      • 2012-11-03
      相关资源
      最近更新 更多