Libpuzzle索引数百万张图片?

时间:2012-03-14 14:19:33

标签: php mysql compare similarity image

关于来自Frank Denis先生的php(http://libpuzzle.pureftpd.org/project/libpuzzle)的libpuzzle libray。我试图了解如何在我的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";
}

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

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

  

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

     

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

     

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

     

通过单词和位置进行索引也可以轻松拆分   将数据分成多个表和服务器。

     

所以是的,Puzzle库与cert不相容   需要索引数百万张图片的项目。

我也发现了关于索引的描述:

  

------------------------ INDEXING ---------------------- -

     

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

     

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

     

用固定长度的单词剪切矢量。例如,让我们考虑一下   以下向量:

     

[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]   在第2位发现的位置1 [c d e f g h i j k l]   直到位置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合作的人。

干杯。

4 个答案:

答案 0 :(得分:14)

所以,让我们看看他们给出的例子并尝试扩展。

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

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");

您可以通过添加需要最少HAVING的{​​{1}}子句来改进查询,从而进一步减少匹配集。

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

基本上,以这种方式拆分和存储单词允许您进行粗略的距离检查,而无需在签名上运行专门的功能。

答案 1 :(得分:3)

我之前已尝试过libpuzzle - 得到了你的意见。并没有真正开始正确的实施。还不清楚究竟如何做到这一点。 (并且由于时间不够而放弃了项目 - 因此并没有真正坚持下去)

无论如何,现在看,将尝试提供我的理解 - 也许在我们之间我们可以解决它:)

查询使用2阶段过程 -

  1. 首先使用单词表。
    1. 拍摄“参考”图像并制作出其签名。
    2. 计算出其组成词,
    3. 查阅字词表,找到所有可能的匹配项。这可以使用数据库引擎的“索引”进行有效查询。
    4. 编译所有sig_ids的列表。 (将在3.中得到一些重复。)
  2. 然后查阅签名
    1. 签名中进行所有可能的检索和解压缩(因为您有预先过滤的列表,数字应该相对较小)
    2. 使用puzzle_vector_normalized_distance来计算实际距离。
    3. 按要求对结果进行排序和排名
  3. (即您只在签名表上使用压缩。单词保持未压缩状态,因此可以对其运行快速查询)

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

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

答案 2 :(得分:1)

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

&#13;
&#13;
// 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);
}
&#13;
&#13;
&#13;

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

如果您阅读了libpuzzle所基于的原始文章(An Image Signature for any kind of Image),他们会解释应该生成单词&#34; ...可能是非连续且重叠的 &#34 ;. 我不确定它们是否意味着不连续且不重叠,或非连续且重叠......

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

很想听听你们的理解。

答案 3 :(得分:0)

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

该项目采用杰森提出的方式。

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


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

enter image description here enter image description here

现在我们有两张图片,让我计算一下他们的签名。

enter image description here

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

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

...


我的英语太差了,所以我不能表达我的想法继续......我想任何想要索引数百万张图片的人都应该检查我的GitHub回购libpuzzle DEMO ..