08月13, 2011

Imghash:相似图片搜索的php实现

前几天看到阮一峰的博客里写到关于相似图片搜索的原理, 里面介绍了Google相似图片搜索的大致原理,主要是从Neal Krawetz博士的LOOKS LIKE IT翻译而来。文章详细的介绍了相似图片搜索处理的相关步骤,并且给出了一个python的实现,由于我对PHP比较熟,所以写了个PHP的版本。

图片相似搜索的简单原理

根据文章里的描述,其实原理比较简单,大致有如下几个步骤:

1、缩小尺寸。将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。

2、简化色彩。将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。

3、计算平均值。计算所有64个像素的灰度平均值。

4、比较像素的灰度。将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。

5、计算哈希值。将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。

这种算法的优点是简单快速,不受图片大小缩放的影响,缺点是图片的内容不能变更。实际应用中,往往采用更强大的pHash算法和SIFT算法,它们能够识别图片的变形。只要变形程度不超过25%,它们就能匹配原图。

图片相似搜索的PHP实现

原文中给出了一个python版本的实现,代码只有53行。我写了个对应的PHP版本,由于直接是用类写的,所以代码有100行,具体如下:

<?php
/**

  • 相似图片搜索hash的php实现
  • @author welefen / class Imghash{

    private static $_instance = null;

    public $rate = 2;

    public static function getInstance(){

     if (self::$_instance === null){
         self::$_instance = new self();
     }
     return self::$_instance;

    } public function run($file){

     if (!function_exists('imagecreatetruecolor')){
         throw new Exception('must load gd lib', 1);
     }
     $isString = false;
     if (is_string($file)){
         $file = array($file);
         $isString = true;
     }
     $result = array();
     foreach ($file as $f){
         $result[] = $this->hash($f);
     }
     return $isString ? $result[0] : $result;

    } public function checkIsSimilarImg($imgHash, $otherImgHash){

     if (file_exists($imgHash) && file_exists($otherImgHash)){
         $imgHash = $this->run($imgHash);
         $otherImgHash = $this->run($otherImgHash);
     }
     if (strlen($imgHash) !== strlen($otherImgHash)) return false;
     $count = 0;
     $len = strlen($imgHash);
     for($i=0;$i<$len;$i++){
         if ($imgHash{$i} !== $otherImgHash{$i}){
             $count++;
         }
     }
     return $count <= (5 * $rate * $rate) ? true : false;

    } public function hash($file){

     if (!file_exists($file)){
         return false;
     }
     $height = 8 * $this->rate;
     $width = 8 * $this->rate;
     $img = imagecreatetruecolor($width, $height);
     list($w, $h) = getimagesize($file);
     $source = $this->createImg($file);
     imagecopyresampled($img, $source, 0, 0, 0, 0, $width, $height, $w, $h);
     $value = $this->getHashValue($img);
     imagedestroy($img);
     return $value;

    } public function getHashValue($img){

     $width = imagesx($img);
     $height = imagesy($img);
     $total = 0;
     $array = array();
     for ($y=0;$y<$height;$y++){
         for ($x=0;$x<$width;$x++){
             $gray = ( imagecolorat($img, $x, $y) >> 8 ) & 0xFF;
             if (!is_array($array[$y])){
                 $array[$y] = array();
             }
             $array[$y][$x] = $gray;
             $total += $gray;
         }
     }
     $average = intval($total / (64 * $this->rate * $this->rate));
     $result = '';
     for ($y=0;$y<$height;$y++){
         for ($x=0;$x<$width;$x++){
             if ($array[$y][$x] >= $average){
                 $result .= '1';
             }else{
                 $result .= '0';
             }
         }
     }
     return $result;

    } public function createImg($file){

     $ext = $this->getFileExt($file);
     if ($ext === 'jpeg') $ext = 'jpg';
     $img = null;
     switch ($ext){
         case 'png' : $img = imagecreatefrompng($file);break;
         case 'jpg' : $img = imagecreatefromjpeg($file);break;
         case 'gif' : $img = imagecreatefromgif($file);
     }
     return $img;

    } public function getFileExt($file){

     $infos = explode('.', $file);
     $ext = strtolower($infos[count($infos) - 1]);
     return $ext;

    } }

调用方式如下:
require_once "Imghash.class.php";
$instance = ImgHash::getInstance();
$result = $instance->checkIsSimilarImg('chenyin/IMG_3214.png', 'chenyin/IMG_3212.JPG');
如果$result值为true, 则表明2个图片相似,否则不相似。

如果你对这个比较感兴趣,你可以从这里下载PHP代码

其他

在实际的相似图片搜索中,算图片的指纹并不是难点,难点而是在怎么从海量的图片指纹里找出与之相似的指纹。

目前有图片相似搜索功能的有:Google图片搜索    tineye    百度识图

本文链接:http://welefen.com/post/similar-image-search-in-php.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。