【PHP】对比两张图片的相似度

发布于:2025-08-05 ⋅ 阅读:(17) ⋅ 点赞:(0)

目录

一、需求

二、相似度算法

三、示例

1、差异值哈希

2、均值哈希

四、计算结果

1、差异值哈希

2、均值哈希


一、需求

给出两张图片,对比这两张图片的相似度,如果相似度大于设定的某个阈值(比如说大于80),就可以判断两张图片是类似的,如果小于某个阈值说明两张图片差异较大。

下面的示例使用的是ThinkPHP5。

二、相似度算法

下面介绍两种计算相似度的方法,一种是差异值哈希,一种是均值哈希。

1、差异值哈希

  • 将图片缩小为8*8的尺寸
  • 将小图片变为灰度图像
  • 计算每个像素的灰度的差值
  • 将两个图片的指纹依次进行比较

2、均值哈希

  • 将图片缩小为8*8的尺寸
  • 将小图片变为灰度图像
  • 计算每个像素的灰度平均值
  • 与平均值进行比较,大于等于为 1,小于为 0,得到指纹
  • 将两个图片的指纹依次进行比较,相同 count++  count越大,相似度越高

两种算法均是先把图片缩小,变成灰度图像,差异值哈希是计算每个像素的灰度差值,再将两张图片依次进行比较,均值哈希算法是计算每个像素的灰度平均值,再与平均值作比较,再将两张图片的指纹依次对比

三、示例

1、差异值哈希

<?php
namespace app\index\controller;
use think\Controller;

/* 差异值哈希
 * 1.将图片缩小为8*8的尺寸
 * 2.将小图片变为灰度图像
 * 3.计算每个像素的灰度的差值
 * 4.将两个图片的指纹依次进行比较
 * */

class ImgCompareDHASH extends Controller
{
	//正方形图片
    public $width = 8;
    public $height = 8;
    public $scalar = 8;

    /**
     * 主方法 - 对比两张图片,得到相似度  -- 步骤4
     */
    public function compare($img1, $img2){

        $hash1 = $this->gethash($img1);
        $hash2 = $this->gethash($img2);
        if(strlen($hash1) !== strlen($hash2)) {
            return false;
        }
        $result = $this->hd($hash1, $hash2);
        //计算相似度
        $result_percent = ($result / ($this->scalar * $this->scalar)) * 100;
        return $result_percent;

    }

    /**
     * 计算汉明距离
     */
    public function hd($h1, $h2)
    {
        $len = strlen($h1);
        $dist = 0;
        for ($i = 0;$i < $len; $i++) {
            if ( $h1[$i] == $h2[$i] )
                $dist++;
        }
        return $dist;
    }

    // 获得图片指纹
    public function gethash($url){

        /**
         * 新建一个 width * height 真彩色图像  -- 步骤1
         * 返回一个图像标识符
         */
        $new_img = imagecreatetruecolor($this->width, $this->height);   

        //获取图片宽和高
        list($ex_w, $ex_h) = getimagesize($url);
         // 获得图片文件的后缀名
        $name = pathinfo($url, PATHINFO_EXTENSION);

        $ex_img = call_user_func('imagecreatefrom'. ( $name == 'jpg' ? 'jpeg' : $name ) , $url);

        //调整图片尺寸 - 重采样拷贝部分图像并调整大小
        imagecopyresampled($new_img, $ex_img, 0, 0, 0, 0, $this->width, $this->height, $ex_w, $ex_h);

        //转换图片为灰度图  -- 步骤2
        imagefilter($new_img, IMG_FILTER_GRAYSCALE);
        
        //销毁缓存
        imagedestroy($ex_img);

        //记录像素
        $pixels = [];
        for($i = 0; $i < $this->scalar; $i++){
            for($j = 0; $j < $this->scalar; $j++){

                /**
                 * 获得每个位置像素的索引值
                 * 0xFF:表示16进制 相当于 十进制 的 255
                 * 1111 1111
                 * 计算图片灰度值
                 */
                $gray = ImageColorAt($new_img, $i, $j) & 255;
                //记录每个点的像素值
                $pixels[] = $gray;     
            }
        }

        imagedestroy($new_img);

        // 计算所有像素的灰阶平均值  -- 步骤3   
       $average = intval(array_sum($pixels) / count($pixels));    
    
       //获取图片指纹
       $hashStr = '';    
       foreach ($pixels as $gray){    
           $hashStr .= ($gray >= $average) ? '1' : '0';
       }
       return $hashStr;

    }
}

2、均值哈希

<?php
namespace app\index\controller;
use think\Controller;

/* 均值哈希
 * 1.将图片缩小为8*8的尺寸
 * 2.将小图片变为灰度图像
 * 3.计算每个像素的灰度平均值
 * 4.与平均值进行比较,大于等于为 1,小于为 0,得到指纹
 * 5.将两个图片的指纹依次进行比较,相同 count++  count越大,相似度越高
 * */

class ImgCompareAVG extends Controller
{
	// 比较相似度  实现步骤5
    public function compare($img1,$img2){

        static $self;
        if(!$self) $self = new static;
        $hash1 = $self->gethash($img1);
        $hash2 = $self->gethash($img2);
        if(strlen($hash1) !== strlen($hash2)) return false;
        $count = 0;
        $len = strlen($hash1);
        for($i = 0; $i < $len; $i++){
            if($hash1[$i] == $hash2[$i]){
                $count++;
            }
        }
        return $count;      // 相当于返回相似度

    }

    // 将图片文件返回为图像标识符 代表图片
    public function getimg($url){
        $name = pathinfo($url,PATHINFO_EXTENSION);    // 获得图片文件的后缀名
        $img = call_user_func('imagecreatefrom'. ( $name == 'jpg' ? 'jpeg' : $name ) , $url);   // 由文件创建图片
        return $img;
    }

    // 获得图片指纹
    public function gethash($url){

        $array = array();
        $total = 0;

        $new_img = imagecreatetruecolor(8,8);   // 建立一个 8*8 的黑色图像
        list($ex_w,$ex_h) = getimagesize($url);  // 将获得的图像长宽赋值给 $ex_w  $ex_h
        $ex_img = $this->getimg($url);  //  获得图片
        imagecopyresampled($new_img, $ex_img,0,0,0,0,8,8,$ex_w,$ex_h);  //  实现步骤 1 2
        imagedestroy($ex_img);   // 销毁原图

        for($i=0;$i<8;$i++){
            for($j=0;$j<8;$j++){
                $gray = (imagecolorat($new_img, $j, $i) >> 8) & 0xFF;     // 获得每个位置像素的索引值   0xFF  1111 1111
                $array[$i][$j] = $gray;     // 记录每个点的像素值
                $total += $gray;    // 计算总和
            }
        }

        imagedestroy($new_img);
        $average = intval($total / (8 * 8 * 2));   // 平均值  实现步骤3
        $hash = '';
        for($i=0;$i<8;$i++){
            for($j=0;$j<8;$j++){
                $hash .= ($array[$i][$j] >= $average) ? '1' : '0';    // 实现步骤4
            }
        }

        return$hash;
    }
    
}

四、计算结果

输入的两张图片

1、差异值哈希

2、均值哈希

这两种算法是通过计算汉明距离或相同位数来评估相似度,不能代表绝对的相似,但可作为参考,两种算法各有特点,其中差异值哈希计算的结果相对比较可靠。


网站公告

今日签到

点亮在社区的每一天
去签到