main

О сравнении изображений

Что мы обычно понимаем под этой задачей? Разгребание архива фоток, ~/Downloads, и прочих мест, где скапливаются картинки, оставленные "на потом". Ну лень же каждый день из-за 2х скачанных картинок с котиками вспоминать куда где у тебя хранится их подборка. Проходит неделя, месяц, картинок становится не две, а две сотни, и теперь лень чистить это дело, включая выгребание дубликатов и из самой коллекции. В результате - перманентный бардак.

Второе допущение которое я делаю - что этих картинок относительно немного, ну тысяча, две, максимум пять.

Вышеописанный случай с грехом пополам всё-таки покрывается продвинутым просмотрщиком изображений, хоть под линуксом их можно пересчитать по пальцам одной руки, и ещё половина пальцев останется. А что делать тому, у кого счёт идёт на десятки тысяч картинок? Готовых решений тут может и не быть, поэтому я рассмотрю возможные пути решения, приложения и их текущий статус.

libimage-seek-perl

Выглядит просто как топор, надо потестить.

Чтобы облегчить задачу, я брал не сами картинки, а их превьюшки, зажатые в квадрат 200x200 с сохранением пропорций и принудительной перегонкой в жпег. Код примерно следующий:

#!/usr/bin/perl

use strict;
use warnings;

use GD;
use Image::Seek qw(loaddb add_image savedb);

loaddb('test.db');

for (my $i = 1; $i <= 20; $i++) {
  my $image = GD::Image->newFromJpeg("image$i.jpg") or die $!;
  add_image($image, $i);
}
savedb('test.db');
cleardb(); # очистка памяти

Размер превьюшек - ~200 кб, размер базы - 390 kб. Терпимо, но есть пара ньюансов.

  • База грузится в память вся целиком. И каждый раз при сохранении переписывается заново. Очевидно, о tie автор не знал или не осилил.
  • Второе - очень легко наколбасить туда картинок и забыть список айдишников. А те три с половиной функции, что нам экспортируются в качестве api не позволяют этот список получить никак.

Поэтому не рекомендую, база должна порхать как бабочка и жалить как^W^W^W, гхм, о чём я говорил?

GNUift

Если кратко - монстр. С налёту полностью запустить не удалось, несмотря на предарительный напильник debian'овских мейнтейнеров. Посмотел по диагонали исходники, такое впечатление, что писалось в начале 200х или людьми, застрявшими где-то там. Boost, perl, общее впечатление сборки на коленке, например следующий фрагмент:

use lib '/usr/bin';
use CGIFTLink;

В этих 2х строчках нарушается сразу 2 правила "хорошего тона". Во-первых, 'use lib' вообще быть не должно, это хак. Во-вторых, библиотек CGIFT* - несколько, именоваться они должны как CGIFT::Link, ради компактного запихивания в директорию. Хороший пример - IO::*

Фрагмент логов запуска сервера укрепляет нас в мысли о 'наколеночности':

Not testing file:libxmlrpc_abyss.so.3.16 (File name does not match plugin name)
Not testing file:libpangomm-1.4.so.1.0.30 (File name does not match plugin name)
Lib:libGIFTQuHierarchy, to be linked from libGIFTQuHierarchy.so.0 already registered!
Not testing file:libbonoboui-2.so.0 (File name does not match plugin name)
Not testing file:purple-2 (File name does not match plugin name)
Not testing file:libwpg-0.2.so.2 (File name does not match plugin name)

Это оно так ищет свои библиотеки, гребя всё подряд из /usr/lib.

На протоколе общения с сервером (MRML) я остановлюсь особо.

  • автор - GNU, что намекает на злостный overingineering, и всемирные амбиции, поразительно сочетающийся с наколеночностью в данном случае
  • документации тупо нет в интернете, оффсайт mrml.net, который указан везде в ссылках - продан какому-то ООО "Вектор", связанному с займами.
  • xml в качестве базы для протокола. Я не зря вспомнил 2000е, это было время эйфории и мыслей 'xml/sgml нас всех спасёт'. Ещё пример - xmpp.
  • активная разработка закончена ~ в 2004 году, последующие 10 лет появилось несколько недоделанных клиентов ...и всё. Скорее всего причина в безблагодатности кодовой базы.

isk-daemon

Родоначальник библиотеки из первого пункта. То что выше - это порт на Perl::XS. Сама библиотека написана на c++, вебморда на питоне/twisted. Последний релиз - 2012год, притом, по зависимостям тянет libmagick++3, а в вышедшем в прошлом году wheezy - libmagick++5. По этой причине попробовать завести пока не получилось. Но выглядит адекватнее gnuift'а.

findimagedupes

Скриптик на перле, позволяющий худо-бедно искать и сравнивать изображения. Суть алгоритма описана в мане и примерно такова:

  • resize 160x160
  • grayscale
  • blur
  • black&white threshold
  • resize 16x16
  • reduce palette to 1bpp

Таким образом, мы имеем фиксированно 256 байт на каждую картинку + служебные данные вроде пути к ней и сколько-то на служебные данные db. Автор не стал придумывать велосипед и использовал bdb через tie.

Интересен способ, с помощью которого ускоряется сравнение изображений. В комплекте поставляется перловый модуль, но не простой, а со встроенным сишным кодом, который компилится при сборке. При этом не XS, а Inline. Я могу понять нежелание автора городить полноценнную либу и биндинг к ней, там ровно две сишные функции, и два дефайна, но это мешает заюзать эту функцию в других программах.

Ах да, чуть не забыл, для тяжёлых операций с графикой используется вся мощь GraphicsMagick