30 March 2009

Простое сравнение изображений с помощью php

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.

*функция подсчета схожести примерная — лучше данное действие выполнять на стороне базы.

Примеры


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

image
52% схожести

image
28% схожести

image
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.
Tags: php изображения gd
Hubs: PHP
+145
23.5k 359
Comments 64
Ads
Top of the day