PHP图像相似度计算方法详解
在图像处理相关开发中,经常需要判断两张图像是否相似,或者计算它们的相似程度。PHP作为常用的后端开发语言,提供了GD、Imagick等扩展支持图像处理,结合不同的算法可以实现图像相似度计算。本文将介绍三种常用的PHP图像相似度计算算法,并给出对应的实现示例。
一、感知哈希算法(pHash)
感知哈希算法是计算图像相似度的常用方法,核心思路是将图像缩小到固定尺寸,转换为灰度图后计算哈希值,最后通过汉明距离判断相似度。汉明距离越小,说明两张图像越相似。
实现步骤
将图像缩放到固定尺寸(通常为32x32或64x64)
转换为灰度图,计算每个像素的灰度值
计算所有像素灰度值的平均值
将每个像素的灰度值与平均值比较,大于平均值记为1,否则记为0,生成哈希字符串
计算两张图像哈希值的汉明距离,转换为相似度百分比
PHP实现代码
<?php
/**
* 计算图像的感知哈希值
* @param string $imagePath 图像路径
* @param int $size 缩放尺寸,默认32
* @return string 哈希值字符串
*/
function getPerceptualHash($imagePath, $size = 32) {
// 检查图像是否存在
if (!file_exists($imagePath)) {
return '';
}
// 获取图像信息
$imageInfo = getimagesize($imagePath);
if (!$imageInfo) {
return '';
}
// 根据图像类型创建资源
switch ($imageInfo[2]) {
case IMAGETYPE_JPEG:
$img = imagecreatefromjpeg($imagePath);
break;
case IMAGETYPE_PNG:
$img = imagecreatefrompng($imagePath);
break;
case IMAGETYPE_GIF:
$img = imagecreatefromgif($imagePath);
break;
default:
return '';
}
if (!$img) {
return '';
}
// 缩放图像
$resizeImg = imagecreatetruecolor($size, $size);
imagecopyresampled($resizeImg, $img, 0, 0, 0, 0, $size, $size, $imageInfo[0], $imageInfo[1]);
// 转换为灰度图
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < $size; $j++) {
$rgb = imagecolorat($resizeImg, $i, $j);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$gray = round(0.299 * $r + 0.587 * $g + 0.114 * $b);
$grayColor = imagecolorallocate($resizeImg, $gray, $gray, $gray);
imagesetpixel($resizeImg, $i, $j, $grayColor);
}
}
// 计算平均灰度值
$totalGray = 0;
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < $size; $j++) {
$rgb = imagecolorat($resizeImg, $i, $j);
$gray = $rgb & 0xFF;
$totalGray += $gray;
}
}
$avgGray = $totalGray / ($size * $size);
// 生成哈希值
$hash = '';
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < $size; $j++) {
$rgb = imagecolorat($resizeImg, $i, $j);
$gray = $rgb & 0xFF;
$hash .= $gray >= $avgGray ? '1' : '0';
}
}
// 释放资源
imagedestroy($img);
imagedestroy($resizeImg);
return $hash;
}
/**
* 计算汉明距离
* @param string $hash1 哈希值1
* @param string $hash2 哈希值2
* @return int 汉明距离
*/
function hammingDistance($hash1, $hash2) {
$len = strlen($hash1);
if ($len != strlen($hash2)) {
return -1;
}
$distance = 0;
for ($i = 0; $i < $len; $i++) {
if ($hash1[$i] != $hash2[$i]) {
$distance++;
}
}
return $distance;
}
/**
* 计算两张图像的相似度百分比
* @param string $image1 图像1路径
* @param string $image2 图像2路径
* @param int $size 哈希计算缩放尺寸
* @return float 相似度百分比,0-100
*/
function calcPhashSimilarity($image1, $image2, $size = 32) {
$hash1 = getPerceptualHash($image1, $size);
$hash2 = getPerceptualHash($image2, $size);
if (empty($hash1) || empty($hash2)) {
return 0;
}
$distance = hammingDistance($hash1, $hash2);
if ($distance == -1) {
return 0;
}
$maxDistance = $size * $size;
return round((1 - $distance / $maxDistance) * 100, 2);
}
// 使用示例
$image1 = 'test1.jpg';
$image2 = 'test2.jpg';
$similarity = calcPhashSimilarity($image1, $image2);
echo "两张图像的感知哈希相似度为:{$similarity}%";
?>二、直方图相似度算法
直方图相似度算法的核心是统计图像的颜色分布,通过计算两张图像直方图的相似程度来判断图像相似性。该算法对颜色敏感,适合颜色特征明显的图像对比。
实现步骤
将图像缩放为较小尺寸(如64x64),减少计算量
分别统计红、绿、蓝三个通道的像素值分布,将0-255的像素值区间划分为若干等级(如16级)
计算两张图像对应通道直方图的余弦相似度
综合三个通道的相似度得到最终相似度
PHP实现代码
<?php
/**
* 计算图像的颜色直方图
* @param string $imagePath 图像路径
* @param int $size 缩放尺寸,默认64
* @param int $level 直方图等级数,默认16
* @return array 包含r、g、b三个通道直方图数据的数组
*/
function getImageHistogram($imagePath, $size = 64, $level = 16) {
if (!file_exists($imagePath)) {
return [];
}
$imageInfo = getimagesize($imagePath);
if (!$imageInfo) {
return [];
}
switch ($imageInfo[2]) {
case IMAGETYPE_JPEG:
$img = imagecreatefromjpeg($imagePath);
break;
case IMAGETYPE_PNG:
$img = imagecreatefrompng($imagePath);
break;
case IMAGETYPE_GIF:
$img = imagecreatefromgif($imagePath);
break;
default:
return [];
}
if (!$img) {
return [];
}
// 缩放图像
$resizeImg = imagecreatetruecolor($size, $size);
imagecopyresampled($resizeImg, $img, 0, 0, 0, 0, $size, $size, $imageInfo[0], $imageInfo[1]);
// 初始化直方图数组
$rHist = array_fill(0, $level, 0);
$gHist = array_fill(0, $level, 0);
$bHist = array_fill(0, $level, 0);
// 统计像素值
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < $size; $j++) {
$rgb = imagecolorat($resizeImg, $i, $j);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
// 计算所属的等级
$rLevel = intval($r / (256 / $level));
$gLevel = intval($g / (256 / $level));
$bLevel = intval($b / (256 / $level));
// 边界处理
if ($rLevel >= $level) $rLevel = $level - 1;
if ($gLevel >= $level) $gLevel = $level - 1;
if ($bLevel >= $level) $bLevel = $level - 1;
$rHist[$rLevel]++;
$gHist[$gLevel]++;
$bHist[$bLevel]++;
}
}
imagedestroy($img);
imagedestroy($resizeImg);
return ['r' => $rHist, 'g' => $gHist, 'b' => $bHist];
}
/**
* 计算两个直方图的余弦相似度
* @param array $hist1 直方图1
* @param array $hist2 直方图2
* @return float 相似度,0-1之间
*/
function histogramCosineSimilarity($hist1, $hist2) {
$len = count($hist1);
if ($len != count($hist2)) {
return 0;
}
$dotProduct = 0;
$norm1 = 0;
$norm2 = 0;
for ($i = 0; $i < $len; $i++) {
$dotProduct += $hist1[$i] * $hist2[$i];
$norm1 += $hist1[$i] * $hist1[$i];
$norm2 += $hist2[$i] * $hist2[$i];
}
if ($norm1 == 0 || $norm2 == 0) {
return 0;
}
return $dotProduct / (sqrt($norm1) * sqrt($norm2));
}
/**
* 计算两张图像的直方图相似度
* @param string $image1 图像1路径
* @param string $image2 图像2路径
* @return float 相似度百分比,0-100
*/
function calcHistogramSimilarity($image1, $image2) {
$hist1 = getImageHistogram($image1);
$hist2 = getImageHistogram($image2);
if (empty($hist1) || empty($hist2)) {
return 0;
}
$rSim = histogramCosineSimilarity($hist1['r'], $hist2['r']);
$gSim = histogramCosineSimilarity($hist1['g'], $hist2['g']);
$bSim = histogramCosineSimilarity($hist1['b'], $hist2['b']);
// 三个通道取平均,转换为百分比
return round(($rSim + $gSim + $bSim) / 3 * 100, 2);
}
// 使用示例
$image1 = 'test1.jpg';
$image2 = 'test2.jpg';
$similarity = calcHistogramSimilarity($image1, $image2);
echo "两张图像的直方图相似度为:{$similarity}%";
?>三、像素对比算法
像素对比算法是最直接的相似度计算方式,将两张图像缩放到相同尺寸后,逐个像素比较RGB值的差异,计算平均差异后转换为相似度。该方法精度较高,但计算量相对较大,适合小尺寸图像对比。
实现步骤
将两张图像缩放到相同的固定尺寸(如32x32)
逐个像素对比两张图像的R、G、B值,计算每个通道的差值绝对值
计算所有像素差值的均值,转换为相似度百分比
PHP实现代码
<?php
/**
* 计算两张图像的像素对比相似度
* @param string $image1 图像1路径
* @param string $image2 图像2路径
* @param int $size 缩放尺寸,默认32
* @return float 相似度百分比,0-100
*/
function calcPixelSimilarity($image1, $image2, $size = 32) {
if (!file_exists($image1) || !file_exists($image2)) {
return 0;
}
$info1 = getimagesize($image1);
$info2 = getimagesize($image2);
if (!$info1 || !$info2) {
return 0;
}
// 创建图像资源
switch ($info1[2]) {
case IMAGETYPE_JPEG:
$img1 = imagecreatefromjpeg($image1);
break;
case IMAGETYPE_PNG:
$img1 = imagecreatefrompng($image1);
break;
case IMAGETYPE_GIF:
$img1 = imagecreatefromgif($image1);
break;
default:
return 0;
}
switch ($info2[2]) {
case IMAGETYPE_JPEG:
$img2 = imagecreatefromjpeg($image2);
break;
case IMAGETYPE_PNG:
$img2 = imagecreatefrompng($image2);
break;
case IMAGETYPE_GIF:
$img2 = imagecreatefromgif($image2);
break;
default:
return 0;
}
if (!$img1 || !$img2) {
return 0;
}
// 缩放图像到相同尺寸
$resize1 = imagecreatetruecolor($size, $size);
$resize2 = imagecreatetruecolor($size, $size);
imagecopyresampled($resize1, $img1, 0, 0, 0, 0, $size, $size, $info1[0], $info1[1]);
imagecopyresampled($resize2, $img2, 0, 0, 0, 0, $size, $size, $info2[0], $info2[1]);
// 计算像素差异
$totalDiff = 0;
$pixelCount = $size * $size;
for ($i = 0; $i < $size; $i++) {
for ($j = 0; $j < $size; $j++) {
$rgb1 = imagecolorat($resize1, $i, $j);
$rgb2 = imagecolorat($resize2, $i, $j);
$r1 = ($rgb1 >> 16) & 0xFF;
$g1 = ($rgb1 >> 8) & 0xFF;
$b1 = $rgb1 & 0xFF;
$r2 = ($rgb2 >> 16) & 0xFF;
$g2 = ($rgb2 >> 8) & 0xFF;
$b2 = $rgb2 & 0xFF;
// 每个通道的差值绝对值之和,最大差值为255*3
$diff = abs($r1 - $r2) + abs($g1 - $g2) + abs($b1 - $b2);
$totalDiff += $diff;
}
}
// 释放资源
imagedestroy($img1);
imagedestroy($img2);
imagedestroy($resize1);
imagedestroy($resize2);
// 最大总差值为 $pixelCount * 255 * 3
$maxDiff = $pixelCount * 255 * 3;
$similarity = round((1 - $totalDiff / $maxDiff) * 100, 2);
return $similarity;
}
// 使用示例
$image1 = 'test1.jpg';
$image2 = 'test2.jpg';
$similarity = calcPixelSimilarity($image1, $image2);
echo "两张图像的像素对比相似度为:{$similarity}%";
?>四、算法对比与选择建议
三种算法的特点和使用场景各不相同,开发者可以根据实际需求选择:
| 算法类型 | 计算速度 | 精度 | 适用场景 |
|---|---|---|---|
| 感知哈希算法 | 快 | 中等 | 需要快速判断图像是否相似,对轻微修改(如缩放、压缩)不敏感的场景 |
| 直方图相似度算法 | 中等 | 中等偏上 | 颜色特征明显的图像对比,如风景、物品类图像 |
| 像素对比算法 | 慢 | 高 | 小尺寸图像,需要高精度对比的场景 |
如果需要更高精度的图像相似度计算,也可以结合多种算法的结果进行综合判断,比如在感知哈希相似度高于阈值后再使用像素对比算法进一步验证。
注意事项
使用GD扩展前需要确保PHP已开启GD扩展,可以通过
phpinfo()函数查看扩展状态处理大尺寸图像时建议先缩放,避免占用过多内存
不同算法的阈值需要根据实际测试调整,例如感知哈希的汉明距离小于5可以认为图像高度相似
如果图像存在旋转、裁剪等大幅修改,上述算法可能不适用,需要结合图像特征点匹配等更复杂的方案