PHP实现图片相似度比较的感知哈希算法示例

今天早睡
今天早睡
发布时间:2026-10-14| 6人浏览过
原创

针对现有图片搜索 API 不符合轻量化需求的场景,我们可以基于感知哈希(Perceptual Hash)算法,利用 PHP 原生 GD 函数手写一个简单的图片相似度比较模块。

算法原理与准备

该方案的核心逻辑是将复杂的图像内容压缩为一个短小的二进制字符串(即“图像指纹”)。如果两张图片相似,它们的指纹也应该非常接近。具体操作遵循以下步骤:

  1. 缩放:将图片缩小至固定尺寸(如 8x8 或 32x32),忽略长宽比,保留整体轮廓与色彩分布。
  2. 灰度化:将缩小后的彩色图片转为灰度值,仅保留亮度信息。
  3. 计算均值:计算所有像素灰度值的平均数。
  4. 生成 Hash:将每个像素与平均值对比,高于平均值为 1,低于为 0,拼接成哈希字符串。
  5. 比较距离:通过计算两个哈希字符串不同位的数量(汉明距离)来判断相似度。

第1步:定义配置参数

首先创建 ImageHash 类,并设置可调的全局参数:

  • $rate:取样倍率。基础尺寸为 8,最终缩放尺寸为 8 * $rate 正方形。
  • $similarity:相似度阈值(0-100)。汉明距离允许的误差范围百分比。
  • $_createFunc:支持的文件类型映射表,用于动态调用对应的图像加载函数。
class ImageHash {
    public static $rate = 2; // 默认生成 16x16 的哈希特征
    public static $similarity = 80;
    
    private static $_createFunc = array(
        IMAGETYPE_GIF   => 'imageCreateFromGIF',
        IMAGETYPE_JPEG  => 'imageCreateFromJPEG',
        IMAGETYPE_PNG   => 'imageCreateFromPNG',
        IMAGETYPE_BMP   => 'imageCreateFromBMP',
        IMAGETYPE_WBMP  => 'imageCreateFromWBMP',
        IMAGETYPE_XBM   => 'imageCreateFromXBM',
    );
}

第2步:实现图片资源加载

编写静态方法 createImage,负责校验文件存在性、检测格式并返回 GD 资源句柄。

    public static function createImage($filePath) {
        if (!file_exists($filePath)) { return false; }
        
        $type = exif_imagetype($filePath);
        if (!array_key_exists($type, self::$_createFunc)) { return false; }
        
        $func = self::$_createFunc[$type];
        if (!function_exists($func)) { return false; }
        
        return $func($filePath);
    }

第3步:核心哈希计算逻辑

hashImage 方法接收图片资源,执行缩放、灰度提取和指纹生成。注意在结束后销毁临时 GD 图像对象以防止内存泄漏。

    public static function hashImage($src) {
        if (!$src) { return false; }
        
        // 1. 缩略处理
        $delta = 8 * self::$rate;
        $img = imageCreateTrueColor($delta, $delta);
        imageCopyResized($img, $src, 0, 0, 0, 0, $delta, $delta, imagesX($src), imagesY($src));
        
        // 2. 计算灰阶数组
        $grayArray = array();
        for ($y=0; $y<$delta; $y++) {
            for ($x=0; $x<$delta; $x++) {
                $rgb = imagecolorat($img, $x, $y);
                $col = imagecolorsforindex($img, $rgb);
                // RGB 转灰度
                $gray = intval(($col['red'] + $col['green'] + $col['blue']) / 3) & 0xFF;
                $grayArray[] = $gray;
            }
        }
        imagedestroy($img);
        
        // 3. 计算平均灰度
        $average = array_sum($grayArray) / count($grayArray);
        
        // 4. 生成哈希字符串
        $hashStr = '';
        foreach ($grayArray as $gray) {
            $hashStr .= ($gray >= $average) ? '1' : '0';
        }
        
        return $hashStr;
    }

第4步:封装文件操作接口

提供便捷的静态方法直接操作文件路径。

    public static function hashImageFile($filePath) {
        $src = self::createImage($filePath);
        if (!$src) return false;
        $hashStr = self::hashImage($src);
        imagedestroy($src);
        return $hashStr;
    } 
    
    public static function isImageFileSimilar($pathA, $pathB) {
        $hashA = self::hashImageFile($pathA);
        $hashB = self::hashImageFile($pathB);
        return self::isHashSimilar($hashA, $hashB);
    }

第5步:相似度比对(汉明距离)

比对两段二进制哈希字符串,统计不同字符的数量。若差值小于允许阈值,则判定为相似。

    public static function isHashSimilar($aHash, $bHash) {
        $len = strlen($aHash);
        if ($len !== strlen($bHash)) { return false; }
        
        // 计算允许的最大差异位数量
        $maxDiff = $len * (100 - self::$similarity) / 100;
        
        $distance = 0;
        for ($i = 0; $i < $len; $i++) {
            if ($aHash{$i} !== $bHash{$i}) { 
                $distance++; 
            }
        }
        
        return ($distance <= $maxDiff);
    }

使用示例

以下代码演示了如何实例化该类并进行两张图片的相似度验证:

$aPath = 'test/a.jpg';
$bPath = 'test/b.jpg';
$cPath = 'test/c_different.jpg';

// 方式一:分别获取 Hash 后比对
$hashA = ImageHash::hashImageFile($aPath);
$hashB = ImageHash::hashImageFile($bPath);
echo 'A与B相似: ', ImageHash::isHashSimilar($hashA, $hashB) ? 'Yes' : 'No', PHP_EOL;

// 方式二:直接比对文件
echo 'A与C相似: ', ImageHash::isImageFileSimilar($aPath, $cPath) ? 'Yes' : 'No', PHP_EOL;

注意事项与局限性

  • 抗干扰能力:该算法对光照变化和几何变换(如旋转、平移、轻微缩放)具有较强的鲁棒性,因为它依赖于直方图统计而非绝对像素位置。
  • 不适用的场景:对于经过裁剪、严重模糊或色彩大幅修改的图片,效果可能不佳。
  • 性能考量:对于海量图片比对,建议将生成的 Hash 存入数据库,并在应用层进行索引过滤。
以上就是PHP简易版图片相似度比较算法的实现详细内容,更多关于图片处理与哈希算法的资料请关注本站其它相关文章!
相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。