Поиск похожих изображений в PostgreSQL через ImgSmlr
ImgSmlr — это расширение PostgreSQL, которое реализует функцию поиска похожих изображений.
Метод ImgSmlr основан на вейвлет-преобразовании Хаара. Цель ImgSmlr не в том, чтобы предоставить самые современные методы поиска похожих изображений. ImgSmlr был написан как образец расширения, который иллюстрирует, как расширяемость PostgreSQL может покрывать такие нетипичные для СУБД задачи, как поиск похожих изображений.
Как сказано в документации, для установки ImgSmlr, необходимо, чтобы были выполнены следующие условия:
- PostgreSQL версии 9.1 или выше. Чтобы узнать свою версию, достаточно вбить
psql --version
в консоль (терминал). Например, у меня версия 11.14. - У вас установлен пакет разработки PostgreSQL или вы собрали PostgreSQL из исходников.
- В вашей системе установлена библиотека gd2.
- Ваша переменная PATH настроена так, что доступна команда pg_config
Установка происходит также как и указано в README репозитории:
$ git clone https://github.com/postgrespro/imgsmlr.git
$ cd imgsmlr
$ make USE_PGXS=1
$ sudo make USE_PGXS=1 install
$ make USE_PGXS=1 installcheck
$ psql DB -c "CREATE EXTENSION imgsmlr;"
Что будет доступно?
После установки расширения, Вам будет доступно несколько новых функций, 2 типа данных и оператор сравнения <->
.
Типы данных:
pattern
. Длина — 16388 байт. Результат вейвлет-преобразования Хаара на изображенииsignature
. Длина — 64 байта. Краткое представление шаблона для быстрого поиска с использованием индексов GiST
Функции:
Есть набор функций *2pattern(bytea)
, которые преобразуют двоичные данные заданного формата в шаблон. Преобразование в узор состоит из следующих шагов.
- Декомпрессия изображения.
- Преобразования изображения в черно-белое изображение.
- Изменение размера изображения до 64×64 пикселей.
- Применение к изображению вейвлет-преобразование Хаара.
Pattern
можно преобразовать в signature
и перетасовать для меньшей чувствительности к сдвигу изображения.
Функция | Тип возращаемого результата | Описание |
---|---|---|
jpeg2pattern(bytea) | pattern | Преобразование изображения jpeg в pattern |
png2pattern(bytea) | pattern | Преобразование изображения png в pattern |
gif2pattern(bytea) | pattern | Преобразование изображения gif в pattern |
pattern2signature(pattern) | signature | Создание signature (подписи) из pattern (паттерна) |
shuffle_pattern(pattern) | pattern | Перемешать pattern в случайном порядке для меньшей чувствительности к сдвигу изображения |
Типы данных pattern и signature поддерживают оператор <->
для евклидова расстояния. Подпись также поддерживает индексацию GiST с помощью оператора KNN <->.
Оператор | Левый тип | Правый тип | Тип возращаемого результата | Описание |
---|---|---|---|---|
<-> | pattern | pattern | float8 | Евклидово расстояние между двумя паттернами |
<-> | signature | signature | float8 | Евклидово расстояние между двумя подписями |
Идея состоит в том, чтобы найти N лучших похожих изображений по подписи с использованием индекса GiST. Затем найдите первые n (n <N) похожих изображений по шаблону из первых N похожих изображений по подписи.
Как этим пользоваться?
Для начала, создадим таблицу images, в которой будут следующие поля:
- id — сериальный ключ
- image_data — данные фотографии в формате bytea.
- url — ссылка на фотографию. В моём случае это было необходимо.
- extent — тип расширения фотографии png/gif/jpg. Фотографии типа webp можно конвентировать в jpg.
- pattern — сам pattern фотографии
- signature — signature фотографии
Запрос на создание таблицы будет выглядеть так:
CREATE TABLE images (
id SERIAL,
image_data bytea,
url text,
extent varchar(5),
pattern pattern,
signature signature
);
Далее, необходимо создать индекс по полю signature
CREATE INDEX pat_signature_idx ON images USING gist (signature);
Добавление фотографий в таблицу:
Добавлять проще следующим образом
INSERT INTO images(image_data)
SELECT pg_read_binary_file('/home/daniilak/photos/1.jpg');
RETURNING id;
Дальше, в зависимости от вашего типа изображения можно изменить фотографию
UPDATE images
SET
url = 'https://example.com/1.jpg',
pattern = shuffle_pattern(jpeg2pattern(image_data)),
signature = pattern2signature(jpeg2pattern(image_data)),
extent = 'jpg'
WHERE
id = 1;
Поиск похожих фотографий
Вывести 10 похожих фотографий на фотографию под id равным 1.
SELECT
id, url, smlr
FROM
(
SELECT
id, url, smlr, pattern <-> (SELECT pattern FROM images WHERE id = 1) AS smlr
FROM images
WHERE id <> 1
ORDER BY
signature <-> (SELECT signature FROM images WHERE id = 1)
LIMIT 100
) x
ORDER BY x.smlr ASC
LIMIT 10;