Libpuzzle Индексирование миллионов изображений?
его о libpuzzle libray для 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. Эти процедуры занимают много времени (открывайте каждую запись в базе данных - поместите ее в функцию,...)
Я прочитал readme из libbay lib и нашел следующее:
Будет ли он работать с базой данных, содержащей миллионы изображений?
Типичная сигнатура изображения требует только 182 байта, используя встроенную функции сжатия/декомпрессии.
Аналогичные подписи имеют одинаковые слова, т.е. идентичные последовательности значения в тех же позициях. Используя составные индексы (слово + позиция), множество возможных аналогичных векторов резко уменьшено, и в большинстве случаев никакое векторное расстояние фактически не требует вычислить.
Индексирование слов и позиций также позволяет легко разделить данных на несколько таблиц и серверов.
Итак, библиотека "Головоломка", безусловно, несовместима с проекты, которые должны индексировать миллионы изображений.
Также я нашел это описание об индексировании:
------------------------ УКАЗАНИЕ ---------------------- -
Как быстро найти похожие изображения, если они миллионы записей?
Оригинальная бумага имеет простой, но эффективный ответ.
Вырезать вектор в словах фиксированной длины. Например, рассмотрим следующий вектор:
[a b c d e f g h я 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 я j], найденный в позиции 0 [b c d e f g h я j k] найденный в позиции 1 [c d e f g h я 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.
Приветствия.
Ответы
Ответ 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");
Вы можете улучшить запрос, добавив предложение HAVING
, для которого требуется минимум strength
, что приведет к дальнейшему уменьшению вашего соответствия.
Я не гарантирую, что это самая эффективная настройка, но она должна быть примерно функциональной, чтобы выполнить то, что вы ищете.
В принципе, разделение и сохранение слов таким образом позволяет выполнить грубую дистанционную проверку без необходимости запуска специализированной функции для подписей.
Ответ 2
Я экспериментировал с libpuzzle раньше - дошел до вас. На самом деле не началась правильная реализация. Было также непонятно, как именно это сделать. (и отказался от проекта из-за нехватки времени), так что на самом деле он не сохранился)
В любом случае, глядя сейчас, попробуем предложить свое понимание - возможно, между нами мы сможем это сделать:)
Запросы используют двухэтапный процесс -
- сначала вы используете таблицу слов.
- возьмите "ссылочный" образ и выработайте его подпись.
- выработать его составные слова,
- обратитесь к таблице слов, чтобы найти все соответствия . Это может использовать индексы баз данных для эффективных запросов.
- скомпилировать список всех sig_ids. (получится несколько дубликатов в 3.)
- Затем проконсультируйтесь с таблицей подписи
- восстановить и распаковать все возможные из сигнатур (поскольку у вас есть предварительно фильтрованный список, число должно быть относительно небольшим)
- используйте puzzle_vector_normalized_distance для определения фактического расстояния.
- сортировать и ранжировать результаты по мере необходимости
(т.е. вы используете только сжатие в таблице подписей, слова остаются несжатыми, поэтому могут запускать быстрые запросы на нем)
Таблица слов - это форма инвертированного индекса. На самом деле я имею в виду использовать fooobar.com/questions/tagged/... вместо таблицы базы данных слов, потому что это специально разработано как очень быстрый инвертированный индекс.
... в теории в любом случае...
Ответ 3
Я также работаю над libpuzzle в php и имею некоторые сомнения относительно того, как генерировать слова из сигнатур изображения.
Ответ 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);
}
Ответ 4
Я создал проект DEMO на libpuzzle на GitHub: 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..