分享一个PHP简易的图片相似度比较类

记得以前的港片《杀手之王》有一个镜头,就是用计算机判断一个照片和杀手留下的背影照片的相似度,现在我们来分享一个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. class ImageHash {
  19. /**取样倍率 1~10
  20. * @access public
  21. * @staticvar int
  22. * */
  23. public static $rate = 2;
  24. /**相似度允许值 0~64
  25. * @access public
  26. * @staticvar int
  27. * */
  28. public static $similarity = 80;
  29. /**图片类型对应的开启函数
  30. * @access private
  31. * @staticvar string
  32. * */
  33. private static $_createFunc = array(
  34. IMAGETYPE_GIF =>'imageCreateFromGIF',
  35. IMAGETYPE_JPEG =>'imageCreateFromJPEG',
  36. IMAGETYPE_PNG =>'imageCreateFromPNG',
  37. IMAGETYPE_BMP =>'imageCreateFromBMP',
  38. IMAGETYPE_WBMP =>'imageCreateFromWBMP',
  39. IMAGETYPE_XBM =>'imageCreateFromXBM',
  40. );
  41. /**从文件建立图片
  42. * @param string $filePath 文件地址路径
  43. * @return resource 当成功开启图片则传递图片 resource ID,失败则是 false
  44. * */
  45. public static function createImage($filePath){
  46. if(!file_exists($filePath)){ return false; }
  47. /*判断文件类型是否可以开启*/
  48. $type = exif_imagetype($filePath);
  49. if(!array_key_exists($type,self::$_createFunc)){ return false; }
  50. $func = self::$_createFunc[$type];
  51. if(!function_exists($func)){ return false; }
  52. return $func($filePath);
  53. }
  54. /**hash 图片
  55. * @param resource $src 图片 resource ID
  56. * @return string 图片 hash 值,失败则是 false
  57. * */
  58. public static function hashImage($src){
  59. if(!$src){ return false; }
  60. /*缩小图片尺寸*/
  61. $delta = 8 * self::$rate;
  62. $img = imageCreateTrueColor($delta,$delta);
  63. imageCopyResized($img,$src, 0,0,0,0, $delta,$delta,imagesX($src),imagesY($src));
  64. /*计算图片灰阶值*/
  65. $grayArray = array();
  66. for ($y=0; $y<$delta; $y++){
  67. for ($x=0; $x<$delta; $x++){
  68. $rgb = imagecolorat($img,$x,$y);
  69. $col = imagecolorsforindex($img, $rgb);
  70. $gray = intval(($col['red']+$col['green']+$col['blue'])/3)& 0xFF;
  71. $grayArray[] = $gray;
  72. }
  73. }
  74. imagedestroy($img);
  75. /*计算所有像素的灰阶平均值*/
  76. $average = array_sum($grayArray)/count($grayArray);
  77. /*计算 hash 值*/
  78. $hashStr = '';
  79. foreach ($grayArray as $gray){
  80. $hashStr .= ($gray>=$average) ? '1' : '0';
  81. } //开源软件:phpfensi.com
  82. return $hashStr;
  83. }
  84. /**hash 图片文件
  85. * @param string $filePath 文件地址路径
  86. * @return string 图片 hash 值,失败则是 false
  87. * */
  88. public static function hashImageFile($filePath){
  89. $src = self::createImage($filePath);
  90. $hashStr = self::hashImage($src);
  91. imagedestroy($src);
  92. return $hashStr;
  93. }
  94. /**比较两个 hash 值,是不是相似
  95. * @param string $aHash A图片的 hash 值
  96. * @param string $bHash B图片的 hash 值
  97. * @return bool 当图片相似则传递 true,否则是 false
  98. * */
  99. public static function isHashSimilar($aHash, $bHash){
  100. $aL = strlen($aHash); $bL = strlen($bHash);
  101. if ($aL !== $bL){ return false; }
  102. /*计算容许落差的数量*/
  103. $allowGap = $aL*(100-self::$similarity)/100;
  104. /*计算两个 hash 值的汉明距离*/
  105. $distance = 0;
  106. for($i=0; $i<$aL; $i++){
  107. if ($aHash{$i} !== $bHash{$i}){ $distance++; }
  108. }
  109. return ($distance<=$allowGap) ? true : false;
  110. }
  111. /**比较两个图片文件,是不是相似
  112. * @param string $aHash A图片的路径
  113. * @param string $bHash B图片的路径
  114. * @return bool 当图片相似则传递 true,否则是 false
  115. * */
  116. public static function isImageFileSimilar($aPath, $bPath){
  117. $aHash = ImageHash::hashImageFile($aPath);
  118. $bHash = ImageHash::hashImageFile($bPath);
  119. return ImageHash::isHashSimilar($aHash, $bHash);
  120. }
  121. }
  122. ?>