2012-04-23 16:47

[PHP] 簡易的圖片相似度比較

由於相似图片搜索的php实现的 API 不怎麼符合我的用途,所以我重新定義 API 的架構,改寫成比較簡單的函數方式,雖然還是用物件的方式包裝。

  1. <?php 
  2. /** 
  3. * 圖片相似度比較 
  4. * 
  5. * @version     $Id: ImageHash.php 4429 2012-04-17 13:20:31Z jax $ 
  6. * @author      jax.hu 
  7. * 
  8. * <code> 
  9. *  //Sample_1 
  10. *  $aHash = ImageHash::hashImageFile('wsz.11.jpg'); 
  11. *  $bHash = ImageHash::hashImageFile('wsz.12.jpg'); 
  12. *  var_dump(ImageHash::isHashSimilar($aHash, $bHash)); 
  13. * 
  14. *  //Sample_2 
  15. *  var_dump(ImageHash::isImageFileSimilar('wsz.11.jpg', 'wsz.12.jpg')); 
  16. * </code> 
  17. */ 
  18.  
  19. class ImageHash { 
  20.  
  21.    /**取樣倍率 1~10 
  22.     * @access public 
  23.     * @staticvar int 
  24.     * */ 
  25.    public static $rate = 2; 
  26.  
  27.    /**相似度允許值 0~64 
  28.     * @access public 
  29.     * @staticvar int 
  30.     * */ 
  31.    public static $similarity = 80; 
  32.  
  33.    /**圖片類型對應的開啟函數 
  34.     * @access private 
  35.     * @staticvar string 
  36.     * */ 
  37.    private static $_createFunc = array( 
  38.        IMAGETYPE_GIF   =>'imageCreateFromGIF', 
  39.        IMAGETYPE_JPEG  =>'imageCreateFromJPEG', 
  40.        IMAGETYPE_PNG   =>'imageCreateFromPNG', 
  41.        IMAGETYPE_BMP   =>'imageCreateFromBMP', 
  42.        IMAGETYPE_WBMP  =>'imageCreateFromWBMP', 
  43.        IMAGETYPE_XBM   =>'imageCreateFromXBM', 
  44.    ); 
  45.  
  46.  
  47.    /**從檔案建立圖片 
  48.     * @param string $filePath 檔案位址路徑 
  49.     * @return resource 當成功開啟圖片則回傳圖片 resource ID,失敗則是 false 
  50.     * */ 
  51.    public static function createImage($filePath){ 
  52.        if(!file_exists($filePath)){ return false; } 
  53.  
  54.        /*判斷檔案類型是否可以開啟*/ 
  55.        $type = exif_imagetype($filePath); 
  56.        if(!array_key_exists($type,self::$_createFunc)){ return false; } 
  57.  
  58.        $func = self::$_createFunc[$type]; 
  59.        if(!function_exists($func)){ return false; } 
  60.  
  61.        return $func($filePath); 
  62.    } 
  63.  
  64.  
  65.    /**hash 圖片 
  66.     * @param resource $src 圖片 resource ID 
  67.     * @return string 圖片 hash 值,失敗則是 false 
  68.     * */ 
  69.    public static function hashImage($src){ 
  70.        if(!$src){ return false; } 
  71.  
  72.        /*缩小圖片尺寸*/ 
  73.        $delta = 8 * self::$rate; 
  74.        $img = imageCreateTrueColor($delta,$delta); 
  75.        imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src)); 
  76.  
  77.        /*計算圖片灰階值*/ 
  78.        $grayArray = array(); 
  79.        for ($y=0; $y<$delta; $y++){ 
  80.            for ($x=0; $x<$delta; $x++){ 
  81.                $rgb = imagecolorat($img,$x,$y); 
  82.                $col = imagecolorsforindex($img, $rgb); 
  83.                $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF; 
  84.  
  85.                $grayArray[] = $gray; 
  86.            } 
  87.        } 
  88.        imagedestroy($img); 
  89.  
  90.        /*計算所有像素的灰階平均值*/ 
  91.        $average = array_sum($grayArray)/count($grayArray); 
  92.  
  93.        /*計算 hash 值*/ 
  94.        $hashStr = ''; 
  95.        foreach ($grayArray as $gray){ 
  96.            $hashStr .= ($gray>=$average) ? '1' : '0'; 
  97.        } 
  98.  
  99.        return $hashStr; 
  100.    } 
  101.  
  102.  
  103.    /**hash 圖片檔案 
  104.     * @param string $filePath 檔案位址路徑 
  105.     * @return string 圖片 hash 值,失敗則是 false 
  106.     * */ 
  107.    public static function hashImageFile($filePath){ 
  108.        $src = self::createImage($filePath); 
  109.        $hashStr = self::hashImage($src); 
  110.        imagedestroy($src); 
  111.  
  112.        return $hashStr; 
  113.    } 
  114.  
  115.  
  116.    /**比較兩個 hash 值,是不是相似 
  117.     * @param string $aHash A圖片的 hash 值 
  118.     * @param string $bHash B圖片的 hash 值 
  119.     * @return bool 當圖片相似則回傳 true,否則是 false 
  120.     * */ 
  121.    public static function isHashSimilar($aHash, $bHash){ 
  122.        $aL = strlen($aHash); $bL = strlen($bHash); 
  123.        if ($aL !== $bL){ return false; } 
  124.  
  125.        /*計算容許落差的數量*/ 
  126.        $allowGap = $aL*(100-self::$similarity)/100; 
  127.  
  128.        /*計算兩個 hash 值的漢明距離*/ 
  129.        $distance = 0; 
  130.        for($i=0; $i<$aL; $i++){ 
  131.            if ($aHash{$i} !== $bHash{$i}){ $distance++; } 
  132.        } 
  133.  
  134.        return ($distance<=$allowGap) ? true : false; 
  135.    } 
  136.  
  137.  
  138.    /**比較兩個圖片檔案,是不是相似 
  139.     * @param string $aHash A圖片的路徑 
  140.     * @param string $bHash B圖片的路徑 
  141.     * @return bool 當圖片相似則回傳 true,否則是 false 
  142.     * */ 
  143.    public static function isImageFileSimilar($aPath, $bPath){ 
  144.        $aHash = ImageHash::hashImageFile($aPath); 
  145.        $bHash = ImageHash::hashImageFile($bPath); 
  146.        return ImageHash::isHashSimilar($aHash, $bHash); 
  147.    } 
  148.  
  149. } 

2 回應:

Roy 提到...

這個是aHash吧
聽說dHash效果比較好﹐要不要試試?
www.hackerfactor.com/blog/index.php?/archives/529-Kind-of-Like-That.html

Jax Hu 提到...

我實做三種之後,我覺得 pHash 比較優耶!