【问题标题】:Libpuzzle Indexing millions of pictures?Libpuzzle 索引数百万张图片?
【发布时间】:2012-03-31 00:26:34
【问题描述】:

关于来自 Frank Denis 先生的 libpuzzle libray for php (http://libpuzzle.pureftpd.org/project/libpuzzle)。我试图了解如何在我的 mysql 数据库中索引和存储数据。向量的生成是绝对没有问题的。

例子:

# Compute signatures for two images
$cvec1 = puzzle_fill_cvec_from_file('img1.jpg');
$cvec2 = puzzle_fill_cvec_from_file('img2.jpg');

# Compute the distance between both signatures
$d = puzzle_vector_normalized_distance($cvec1, $cvec2);

# Are pictures similar?
if ($d < PUZZLE_CVEC_SIMILARITY_LOWER_THRESHOLD) {
  echo "Pictures are looking similar\n";
} else {
  echo "Pictures are different, distance=$d\n";
}

这对我来说很清楚 - 但是现在当我有大量图片 >1.000.000 时我该如何工作?我计算向量并将其与数据库中的文件名一起存储?现在如何找到相似的图片?如果我将每个向量存储在 mysql 中,我必须打开每条记录并使用 puzzle_vector_normalized_distance 函数计算距离。该过程需要大量时间(打开每个数据库条目 - 将其抛出函数,...)

我从 lib puzzle libaray 中阅读了自述文件,发现以下内容:

它是否适用于拥有数百万张图片的数据库?

一个典型的图像签名只需要 182 个字节,使用内置的 压缩/解压功能。

相似的签名共享相同的“词”,即。相同的序列 相同位置的值。通过使用复合索引(word + 位置),可能的相似向量的集合是显着的 减少,并且在大多数情况下,实际上不需要矢量距离 得到计算。

通过单词和位置进行索引也可以很容易地拆分 数据到多个表和服务器中。

所以是的,Puzzle 库肯定与 需要索引数百万张图片的项目。

我还找到了关于索引的描述:

------------ 索引 ---------- --

如果是百万条记录,如何快速找到相似的图片?

原始论文有一个简单而有效的答案。

将向量切割成固定长度的单词。例如,让我们考虑 以下向量:

[ a b c d e f g h i j k l m n o p q r s t u v w x y z ]

字长(K)为10,可以得到以下单词:

[ a b c d e f g h i j ] 在位置 0 [ b c d e f g h i j k ] 在位置 1 找到 [ c d e f g h i j k l ] 在位置 2 等找到。 直到位置 N-1

然后,使用(单词+位置)的复合索引来索引您的向量。

即使有数百万张图像,K = 10 和 N = 100 也应该足以 很少有条目共享相同的索引。

这是一个非常基本的示例数据库架构:

+-----------------------------+
| signatures |
+-----------------------------+
| sig_id | signature | pic_id |
+--------+-----------+--------+

+--------------------------+
| words |
+--------------------------+
| pos_and_word | fk_sig_id |
+--------------+-----------+

我建议至少将“单词”表拆分为多个 表和/或服务器。

默认情况下 (lambas=9) 签名为 544 字节长。为了节省 存储空间,可以压缩到原来的1/3 通过puzzle_compress_cvec() 函数确定大小。使用前,他们 必须用puzzle_uncompress_cvec() 解压缩。

我认为压缩是错误的方式,因为我必须在比较之前解压缩每个向量。

我现在的问题是 - 处理数百万张图片的方法是什么,以及如何快速有效地比较它们。我无法理解“切割矢量”应该如何帮助我解决我的问题。

非常感谢 - 也许我可以在这里找到正在使用 libpuzzle libaray 的人。

干杯。

【问题讨论】:

    标签: php mysql compare similarity image


    【解决方案1】:

    那么,让我们看看他们给出的示例并尝试扩展。

    假设您有一个存储与每个图像相关的信息(路径、名称、描述等)的表。在该表中,您将包含一个压缩签名字段,在您最初填充数据库时计算和存储。让我们这样定义该表:

    CREATE TABLE images (
        image_id INTEGER NOT NULL PRIMARY KEY,
        name TEXT,
        description TEXT,
        file_path TEXT NOT NULL,
        url_path TEXT NOT NULL,
        signature TEXT NOT NULL
    );
    

    当您最初计算签名时,您还将根据签名计算多个单词:

    // this will be run once for each image:
    $cvec = puzzle_fill_cvec_from_file('img1.jpg');
    $words = array();
    $wordlen = 10; // this is $k from the example
    $wordcnt = 100; // this is $n from the example
    for ($i=0; $i<min($wordcnt, strlen($cvec)-$wordlen+1); $i++) {
        $words[] = substr($cvec, $i, $wordlen);
    }
    

    现在您可以将这些词放入一个表中,定义如下:

    CREATE TABLE img_sig_words (
        image_id INTEGER NOT NULL,
        sig_word TEXT NOT NULL,
        FOREIGN KEY (image_id) REFERENCES images (image_id),
        INDEX (image_id, sig_word)
    );
    

    现在您插入到该表中,在找到单词的位置索引之前添加,以便您知道单词何时匹配它在签名中的同一位置匹配:

    // the signature, along with all other data, has already been inserted into the images
    // table, and $image_id has been populated with the resulting primary key
    foreach ($words as $index => $word) {
        $sig_word = $index.'__'.$word;
        $dbobj->query("INSERT INTO img_sig_words (image_id, sig_word) VALUES ($image_id,
            '$sig_word')"); // figure a suitably defined db abstraction layer...
    }
    

    您的数据如此初始化,您可以相对轻松地抓取匹配单词的图像:

    // $image_id is set to the base image that you are trying to find matches to
    $dbobj->query("SELECT i.*, COUNT(isw.sig_word) as strength FROM images i JOIN img_sig_words
        isw ON i.image_id = isw.image_id JOIN img_sig_words isw_search ON isw.sig_word =
        isw_search.sig_word AND isw.image_id != isw_search.image_id WHERE
        isw_search.image_id = $image_id GROUP BY i.image_id, i.name, i.description,
        i.file_path, i.url_path, i.signature ORDER BY strength DESC");
    

    您可以通过添加要求至少 strengthHAVING 子句来改进查询,从而进一步减少匹配集。

    我不保证这是最有效的设置,但它应该大致可以完成您正在寻找的功能。

    基本上,以这种方式拆分和存储单词可以让您进行粗略的距离检查,而无需对签名运行专门的函数。

    【讨论】:

    • 这是很好的信息 - 谢谢。只是为了澄清一下,您是否真的尝试过这个 - 还是只是“理论上”?不会影响赏金,但绝对有兴趣看到一个有效的实现。特别是您的索引似乎需要调整才能运行有效的查询。
    • 这是理论,我没有直接使用 libpuzzle 的经验,我只是想提供一些代码来扩展 libpuzzle 文档中的示例,主要是作为练习。
    • 快速说明...我们实际上实现了上面的(稍作修改)...就像一个魅力!并且...低看,比运行拼图比较函数图像与图像更准确...到目前为止,我们已经尝试了 20 的强度...并且对于我们的 400 万张强图像,几乎可以获得 100% 准确的结果基地...谢谢!!!
    • 我试过你的代码,但是返回了包含 100 个项目的 $words 数组,没有任何价值?!
    • 我想帮忙,但我不完全清楚你在说什么。 $words 数组是否包含 100 个条目,但每个条目都是一个空白字符串?如果是这样,可能是我打错字了,但是自从我查看此内容以来已经有一年半了,并且不记得所有细节...无论如何,请尝试$words[] = substr($cvec, $i, $wordlen); 并查看是否设置你说得对(如果不是,我显然需要仔细看才能理解我写的内容)
    【解决方案2】:

    我以前尝试过 libpuzzle - 和你差不多。并没有真正开始正确的实施。也不清楚具体怎么做。 (因为没有时间而放弃了这个项目——所以并没有真正坚持下去)

    无论如何,现在看,会尽力提供我的理解 - 也许我们之间可以解决:)

    查询使用 2 阶段过程 -

    1. 首先使用words 表。
      1. 获取“参考”图像并计算其签名。
      2. 算出它的组成词,
      3. 请查阅 words 表以找到所有可能的匹配项。这可以使用数据库引擎“索引”进行高效查询。
      4. 编译所有 sig_id 的列表。 (将在 3 中得到一些重复。)
    2. 然后查阅签名
      1. 签名中检索和解压缩所有可能的内容(因为您有一个预过滤列表,所以数量应该相对较小)
      2. 使用puzzle_vector_normalized_distance 计算出实际距离。
      3. 根据需要对结果进行排序和排名

    (即您只对 signatures 表使用压缩。单词保持未压缩,因此可以对其运行快速查询)

    words表是倒排索引的一种形式。事实上,我想使用https://stackoverflow.com/questions/tagged/sphinx 代替单词数据库表,因为它专门设计为一个非常快速的倒排索引。

    ...理论上无论如何...

    【讨论】:

      【解决方案3】:

      我也在使用 php 开发 libpuzzle,并且对如何从图像签名中生成单词有一些疑问。 Jasons 上面的回答似乎是对的,但我对这部分有疑问:

      // this will be run once for each image:
      $cvec = puzzle_fill_cvec_from_file('img1.jpg');
      $words = array();
      $wordlen = 10; // this is $k from the example
      $wordcnt = 100; // this is $n from the example
      for ($i=0; $i<min($wordcnt, strlen($cvec)-$wordlen+1); $i++) {
          $words[] = substr($cvec, $i, $wordlen);
      }

      签名向量有 544 个字母长,在上面创建的单词中,我们总是只使用它的前 110 个字母。 这意味着如果我理解正确,我们将代表图像内容的上三分之一进行索引。

      如果您阅读 libpuzzle 所基于的原始文章 (An Image Signature for any kind of Image),他们解释说应该生成单词“...可能不连续和重叠”。 我不确定它们是指非连续和非重叠,还是非连续和重叠......

      但如果它们的意思是不重叠,我猜这些词应该分布在整个签名向量中。这也会更有意义,因为向量是通过从左到右、从上到下评估图像区域来创建的。并且通过在整个向量中传播单词意味着您正在考虑整个图像,而不是它的上部(如果您从向量的开头生成所有单词)。

      很想听听你们如何理解这一点。

      【讨论】:

      • 我不确定是否还有人关注这个话题,但对于任何可能提出意见的人......我已经用上述方法做了一些测试
      • 问题是我的前几张测试图像非常相似,我得到的距离为 0.544,这意味着它们应该被判定为几乎相同。但是通过上述从两个图像的签名生成单词的过程,它们的单词都没有重叠。所以这两张图片会在第一步被判定为不相似,这是错误的!
      • 回到上面提到的原始文章,我读到要使“单词步骤”在统计意义上起作用,他们需要将单词中的 -1 和 -2 以及 1 和 2 放在一起向量。我试过了,并且使用 k=10 和 N=100(仅从向量的开头取词,我不确定这是否正确),它几乎不起作用(在非常相似的图像上重叠)对此的任何想法都会很大赞赏。
      • 您应该在这里ask another question 而不是回答。不要犹豫,从这篇文章中复制文字。由于它不是答案,因此不适合问答形式,将其从此处删除只是时间问题。
      【解决方案4】:

      我在 GitHub 上做了一个 libpuzzle DEMO 项目:https://github.com/alsotang/libpuzzle_demo

      该项目使用 Jason 上面提出的方式。

      数据库架构显示在:https://github.com/alsotang/libpuzzle_demo/blob/master/schema.sql


      我将提供有关 libpuzzle 签名的更多信息。

      现在我们有了两张图片,让我计算它们的签名。

      奇数行用于图像 1(左侧),偶数行用于图像 2。

      可以发现,在大多数情况下,相同位置的数字是相同的。

      ....


      我的英语太差了,所以我无法继续表达我的想法......我认为任何想要索引数百万张图片的人都应该检查我的 libpuzzle DEMO 的 GitHub 存储库..

      【讨论】:

      • 可以转成php代码吗?我不知道如何将签名索引到数据库中
      猜你喜欢
      • 1970-01-01
      • 2015-03-15
      • 2021-12-25
      • 1970-01-01
      • 2011-02-03
      • 2023-04-08
      • 2013-04-11
      • 1970-01-01
      • 2019-05-02
      相关资源
      最近更新 更多