Кейсы

Поиск похожих изображений в PostgreSQL  через ImgSmlr

Ссылка на GitHub репотизорий

ImgSmlr — это расширение PostgreSQL, которое реализует функцию поиска похожих изображений.

Метод ImgSmlr основан на вейвлет-преобразовании Хаара. Цель ImgSmlr не в том, чтобы предоставить самые современные методы поиска похожих изображений. ImgSmlr был написан как образец расширения, который иллюстрирует, как расширяемость PostgreSQL может покрывать такие нетипичные для СУБД задачи, как поиск похожих изображений.

Как сказано в документации, для установки ImgSmlr, необходимо, чтобы были выполнены следующие условия:

  1. PostgreSQL версии 9.1 или выше. Чтобы узнать свою версию, достаточно вбить psql --version в консоль (терминал). Например, у меня версия 11.14.
  2. У вас установлен пакет разработки PostgreSQL или вы собрали PostgreSQL из исходников.
  3. В вашей системе установлена библиотека gd2.
  4. Ваша переменная 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 типа данных и оператор сравнения <->.

Типы данных:

  1. pattern. Длина — 16388 байт. Результат вейвлет-преобразования Хаара на изображении
  2. 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 <->.

ОператорЛевый типПравый типТип возращаемого
результата
Описание
<->patternpatternfloat8Евклидово расстояние между двумя паттернами
<->signaturesignaturefloat8Евклидово расстояние между двумя подписями

Идея состоит в том, чтобы найти 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;