一、前言:为什么需要图像哈希?
在当下的互联网环境中,图像数据以爆炸式速度增长。从社交平台的图片分发,到电商平台的商品图采集,再到内容审核、重复图像检测、盗图追踪,如何高效比较两个图像是否相同或相似成为一个核心问题。
我们可以使用深度学习模型如 CNN、ViT 提取图像特征,但这需要 GPU,代价高、复杂度大。而传统图像处理领域提供了一个简单高效的解决方案。
图像感知哈希(Perceptual Hash, pHash)及其系列算法
Python 的 imagehash 库正是目前应用最广、最稳定的一套图像哈希计算工具。
它具有以下特点:
- 速度极快(毫秒级)
- 对缩放、旋转、亮度变化不敏感
- 哈希值可用于相似度比较
- 包含多个算法:aHash、pHash、dHash、wHash
无论你是做:内容去重、图像相似搜索、爬虫去重、图库管理、数字资产管理、图形数据库、以图搜图系统,imagehash 都能胜任。
接下来,我们将从安装、算法原理、使用方法、进阶技巧,逐步完整展开。
二、imagehash 库简介
imagehash 是基于 PIL(即 pillow)的图像感知哈希算法工具包。
安装方式非常简单:
bash
pip install imagehash pillow
常用导入方式:
python
from PIL import Image
import imagehash
它支持四大经典图像哈希算法:
| 哈希算法 | 全称 | 优点 | 使用场景 |
|---|---|---|---|
| aHash | average hash | 简单快速 | 基础相似检测、轻量级应用 |
| pHash | perceptual hash | 最稳健、常用 | 内容审查、重复图检测 |
| dHash | difference hash | 相当稳定 | 图像去重、图像聚类 |
| wHash | wavelet hash | 去噪效果强 | 细节损失较多场景 |
除此之外,它还支持 colorhash、crop-resistant hash 等进阶算法。
三、图像感知哈希的核心思想
传统哈希(如 MD5、SHA256)的特点是:
输入只要有一点点变化,输出就完全不同。
所以不能用来比较图像相似度。
感知哈希的思想正好相反:
当两张图片"看起来相似"时,它们的哈希应当相近。
因此,感知哈希的目标:
- 忽略图像的噪声
- 忽略尺寸变化
- 忽略亮度变化
- 忽略轻微裁剪
- 保留整体结构和视觉内容
最终得到一个通常使用 64 bit(8x8)长度的哈希值:
例:
ff00aa55cc33ee99
两个哈希值之间的差异(Hamming Distance)即可判断图像相似度。
例如:
python
hash1 = imagehash.phash(Image.open("img1.jpg"))
hash2 = imagehash.phash(Image.open("img2.jpg"))
similarity = hash1 - hash2 # 汉明距离
通常距离 <5 可以认为是同一张图片或极其相似。
四、四大常用哈希算法全面解析
接下来我们分别讲解算法原理、特点和代码使用。
1. aHash ------ 平均哈希(Average Hash)
算法流程
- 把图像缩小到 8x8
- 转成灰度
- 求平均值
- 灰度 > 平均值 → 1,否则 → 0
- 得到 64 bit 哈希
特点
- 速度最快
- 简单,可用于海量图片快速预处理
- 对亮度变换不敏感
- 但对轻微变化敏感(裁剪、噪声)
代码示例
python
hash_val = imagehash.average_hash(Image.open('x.jpg'))
print(hash_val)
2. pHash ------ 感知哈希(最经典)
算法流程
- 图像缩放为 32x32
- 转灰度
- 使用 DCT 变换(离散余弦变换)
- 取左上角 8x8 低频区块
- 比较低频分量与均值
特点
- 对缩放、亮度变化非常稳健
- 效果最好、最常用
- 比 aHash 略慢(仍然非常快)
代码示例
python
hash_val = imagehash.phash(Image.open('x.jpg'))
print(hash_val)
3. dHash ------ 差值哈希
算法流程
- 缩放为 9x8
- 比较相邻像素
- 灰度左 > 右 → 1,否则 → 0
特点
- 对结构变化敏感
- 比 pHash 快
- 效果稳定,广泛用于图像去重
代码
python
hash_val = imagehash.dhash(Image.open('x.jpg'))
4. wHash ------ 小波变换哈希(Wavelet Hash)
特点
- 抗噪声能力最强
- 对模糊图、压缩图也稳定
- 稍微比 pHash 慢
使用
python
hash_val = imagehash.whash(Image.open('x.jpg'))
五、哈希值比较:判断两图是否相似
imagehash 的对象支持直接做减法:
python
hash1 = imagehash.phash(Image.open('a.jpg'))
hash2 = imagehash.phash(Image.open('b.jpg'))
distance = hash1 - hash2 # 汉明距离
一般参考阈值:
| 汉明距离 | 相似度判断 |
|---|---|
| 0 | 完全相同 |
| 1-5 | 同一张图片、压缩尺寸不同 |
| 6-10 | 内容相似(例如同一物体) |
| 10 以上 | 基本不相似 |
可自定义阈值:
python
if distance < 5:
print("图片相似")
六、在实战中使用 imagehash:10 个典型场景
1. 大规模图片去重(电商、图库)
适用于:
- 商品图片采集
- 抖音/小红书去重
- 内容审核系统
使用 pHash 或 dHash 最稳定。
2. 内容审核(版权、盗图识别)
可用于发现:
- 相似照片
- 水印图
- 裁剪、调整过的图像
3. 搜索引擎:以图搜图
流程:
- 给每个图库图片生成哈希
- 用户上传图片计算哈希
- 计算差值 <5 的返回
4. 图形数据库(如 Elasticsearch + imagehash)
将哈希存储为字符串即可快速检索。
5. 图像聚类
可用于:
- 人像去重
- 事件聚类
- 相册管理
借助 k-means/DBSCAN 可聚类哈希向量。
6. 监控场景:帧相似度检测
可判断:
- 场景变化
- 异常事件
- 镜头切换
7. 视频抽帧去重
imagehash + ffmpeg:
- 抽帧
- 计算哈希
- 剔除重复
8. 防止重复提交(如合同照片)
可根据哈希查重。
9. 对抗恶搞图片(meme 分析)
字符变换颜色后仍可检测相似。
10. 软件安全与内容完整性验证
判断图标、UI素材是否被篡改。
七、进阶使用:colorhash、crop-resistant-hash 等
imagehash 还提供高级功能:
1. ColorHash(颜色直方图哈希)
适合风景图、背景色分类。
python
hash_val = imagehash.colorhash(Image.open('x.jpg'))
2. Crop-Resistant Hash(最强算法)
对裁剪、长宽变化非常稳健。
适用于:
- 社交平台裁剪图
- 盗图识别
八、与 OpenCV 和 deep learning 的对比
1. 与 OpenCV 的 difference hash 对比
OpenCV 也能写 dHash,但 imagehash 更完整。
2. 与深度学习特征(CNN、CLIP)相比
imagehash 优点:
- 更快(毫秒级)
- 无需 GPU
- 部署简单
- 结果可解释(哈希差)
深度学习优点:
- 支持复杂语义相似度
- 更多场景(如动物、物体检测)
结论:
imagehash 适合结构相似检测,深度学习适合语义相似检测。
在工程中常常两种结合使用。
九、性能优化方案(适合海量数据)
如果要处理百万级图片,可采取:
1. 批处理哈希计算
多线程:
python
from concurrent.futures import ThreadPoolExecutor
def calc(path):
return imagehash.phash(Image.open(path))
with ThreadPoolExecutor(20) as pool:
results = list(pool.map(calc, images))
2. 哈希存储结构优化
存储为:
- 64-bit int
- binary bit array
可加速比较。
3. 使用 Annoy / FAISS 做哈希近似搜索
imagehash 返回的是 64bit bit-array,可作为向量存入 FAISS。
十、完整示例:构建一个图片重复检测系统
以下代码展示一个从目录中自动检测重复图片的完整程序:
python
from PIL import Image
import imagehash
import os
def find_duplicates(folder, threshold=5):
hash_dict = {}
duplicates = []
for fname in os.listdir(folder):
if not fname.lower().endswith(('.jpg', '.png', '.jpeg')):
continue
path = os.path.join(folder, fname)
img = Image.open(path)
h = imagehash.phash(img)
for oh, opath in hash_dict.values():
if h - oh < threshold:
duplicates.append((path, opath))
break
hash_dict[fname] = (h, path)
return duplicates
dups = find_duplicates("images/")
for a, b in dups:
print("相似图片:", a, "<==>", b)
十一、常见问题 FAQ
Q1:不同算法的差异如何选择?
建议:
- 宽松去重:pHash
- 快速去重:dHash
- 抗噪声:wHash
- 色彩特征:colorhash
Q2:为什么同一图片的哈希值有时不同?
可能原因:
- EXIF 旋转信息不同
- 图像压缩/分辨率变化
- 使用了不同算法
Q3:汉明距离阈值如何设定?
经验值:
- 0~5:同一张图
- 5~10:内容相似
-
10:不同
十二、总结:imagehash 仍是图像相似性计算中最值得使用的工具
即使在深度学习时代,imagehash 仍然是工程中最常用的图像相似工具之一,因为它:
- 速度快
- 精度稳定
- 轻量级
- 部署简单
- 跨领域通用
适合作为图像比对的第一道过滤器,再结合深度学习做更高精度分析。