Алгоритм возможно не новый, и не является идеалом, но, на удивление, работает. Никаких графов и корелляции.
Для начала, стоит отметить, что сравнение очень приблизительно (по крайней мере, на данном этапе), временами две совершенно разные фотографии оказываются похожими процентов на 60%, так же не учитываются жесткие модификации (повороты, инверсия, обрезка) – для этого нужно доводить и доводить. Лично для меня самым главным оказались два параметра: скорость, независимость от разрешения изображения и возможность сравнивать «изображения» прямо в базе данных.
В данном случае представлена версия, работающая через GD c PNG изображениями.
*функция подсчета схожести примерная — лучше данное действие выполнять на стороне базы.

На удивление,95% 87% схожести (с добавлением UPD-2)

52% схожести

28% схожести

100% схожести
Всё это только приблительные выкладки, особо продвинутого сравнения таким образом не добиться, но для быстрой выборки из базы похожих изображений сойдет.
P.S. Замечания и советы принимаются.
UPD Спасибо MiniM — код упростился.
UPD-2 Небольшое дополнение:
После этого будет учитываться уменьшение яркости в точке. Таким образом схожесть в первом примере составит 87%.
UPD-3 Перенесено в php.
Для начала, стоит отметить, что сравнение очень приблизительно (по крайней мере, на данном этапе), временами две совершенно разные фотографии оказываются похожими процентов на 60%, так же не учитываются жесткие модификации (повороты, инверсия, обрезка) – для этого нужно доводить и доводить. Лично для меня самым главным оказались два параметра: скорость, независимость от разрешения изображения и возможность сравнивать «изображения» прямо в базе данных.
Принцип
- Открываем исходное изображение
- Масштабируем его до размера маски (в моем случае это 20 на 20, большие размеры мне ни к чему – сравнение приблизительное, вполне возможно сделать маску и 10 на 10).
- Вычисляем основной цвет маски.
- Создаем массив, где значением является ключ типа af2 (1,2 — координты, как в морском бою. 2 — расхождение с основной яркостью).
- Генерируем строку – ключ.
- Сравниваем две строки по релевантности.
Код
В данном случае представлена версия, работающая через GD c PNG изображениями.
//Генерация ключа-изображения
function getimageid($image)
{
//Размеры исходного изображения
$size=getimagesize($image);
//Исходное изображение
$image=imagecreatefrompng($image);
//Маска
$zone=imagecreate(20,20);
//Копируем изображение в маску
imagecopyresized($zone,$image,0,0,0,0,20,20,$size[0],$size[1]);
//Будущая маска
$colormap=array();
//Базовый цвет изображения
$average=0;
//Результат
$result=array();
//Заполняем маску и вычисляем базовый цвет
for($x=0;$x<20;$x++)
for($y=0;$y<20;$y++)
{
$color=imagecolorat($zone,$x,$y);
$color=imagecolorsforindex($zone,$color);
//Вычисление яркости было подсказано хабраюзером Ryotsuke
$colormap[$x][$y]= 0.212671 * $color['red'] + 0.715160 * $color['green'] + 0.072169 * $color['blue'];
$average += $colormap[$x][$y];
}
//Базовый цвет
$average /= 400;
//Генерируем ключ строку
for($x=0;$x<20;$x++)
for($y=0;$y<20;$y++)
$result[]=($x<10?$x:chr($x+97)).($y<10?$y:chr($y+97)).round(2*$colormap[$x][$y]/$average);
//Возвращаем ключ
return join(' ',$result);
}
* This source code was highlighted with Source Code Highlighter.
//Вычисление "похожести" двух изображений
function imagediff($image,$desc)
{
$image=explode(' ',$image);
$desc=explode(' ',$desc);
$result=0;
foreach($image as $bit)
if(in_array($bit,$desc))
$result++;
return $result/((count($image)+count($desc))/2);
}
* This source code was highlighted with Source Code Highlighter.
*функция подсчета схожести примерная — лучше данное действие выполнять на стороне базы.
Примеры

На удивление,

52% схожести

28% схожести

100% схожести
Постскриптум
Всё это только приблительные выкладки, особо продвинутого сравнения таким образом не добиться, но для быстрой выборки из базы похожих изображений сойдет.
P.S. Замечания и советы принимаются.
UPD Спасибо MiniM — код упростился.
UPD-2 Небольшое дополнение:
//Генерируем ключ строку
for($x=0;$x<20;$x++)
for($y=0;$y<20;$y++)
$result[]=($x<10?$x:chr($x+97)).($y<10?$y:chr($y+97)).($colormap[$x][$y]==0?'0':round(2*($colormap[$x][$y]>$average?$colormap[$x][$y]/$average:-1*$average/$colormap[$x][$y])));
* This source code was highlighted with Source Code Highlighter.
После этого будет учитываться уменьшение яркости в точке. Таким образом схожесть в первом примере составит 87%.
UPD-3 Перенесено в php.