Skip to content

图片相似度检测技术综述

1. 图片相似度检测方法概述

图片相似度检测是计算机视觉中的重要任务,广泛应用于图片去重、版权检测、以图搜图等场景。本文介绍几种常见的图片相似度检测方法。

2. 图片哈希算法

图片哈希算法通过计算图片的哈希值来判断相似度,具有计算速度快、占用空间小的优点。

2.1 均值哈希算法(Average Hash, aHash)

原理:

均值哈希算法利用图片的低频信息来生成哈希值。具体步骤:

  1. 缩小尺寸:将图片缩小至 8×8 像素,共 64 个像素点

    • 去除图片细节,只保留结构、明暗等基本信息
    • 消除不同尺寸、比例带来的差异
  2. 转换灰度:将缩小后的图片转为 64 级灰度图

  3. 计算均值:计算所有 64 个像素的灰度平均值

  4. 比较像素:将每个像素的灰度与平均值比较

    • 大于或等于平均值,记为 1
    • 小于平均值,记为 0
  5. 生成指纹:将比较结果组合成 64 位整数,即为图片指纹

特点:

  • ✅ 计算简单快速
  • ✅ 对缩放、旋转有一定鲁棒性
  • ❌ 对亮度变化敏感

2.2 感知哈希算法(Perceptual Hash, pHash)

原理:

  • Block Hash(块哈希):将图片分块后分别计算哈希
  • Median Hash(中值哈希):使用中值代替均值
  • Wavelet Hash(小波哈希):基于小波变换的哈希算法

2.5 实现示例

使用 Python 的 imagehash 库:

python
from PIL import Image
import imagehash

# 读取图片
img1 = Image.open('image1.jpg')
img2 = Image.open('image2.jpg')

# 计算不同类型的哈希
ahash1 = imagehash.average_hash(img1)
ahash2 = imagehash.average_hash(img2)

phash1 = imagehash.phash(img1)
phash2 = imagehash.phash(img2)

dhash1 = imagehash.dhash(img1)
dhash2 = imagehash.dhash(img2)

# 计算汉明距离(值越小越相似)
print(f"Average Hash 距离: {ahash1 - ahash2}")
print(f"Perceptual Hash 距离: {phash1 - phash2}")
print(f"Difference Hash 距离: {dhash1 - dhash2}")

3. 直方图方法

3.1 单通道直方图

计算灰度图像的像素分布直方图,通过比较两个直方图的相似度来判断图片相似度。

python
import cv2
import numpy as np

def compare_histogram(img1_path, img2_path):
    # 读取图片并转换为灰度
    img1 = cv2.imread(img1_path, cv2.IMREAD_GRAYSCALE)
    img2 = cv2.imread(img2_path, cv2.IMREAD_GRAYSCALE)
    
    # 计算直方图
    hist1 = cv2.calcHist([img1], [0], None, [256], [0, 256])
    hist2 = cv2.calcHist([img2], [0], None, [256], [0, 256])
    
    # 归一化
    hist1 = cv2.normalize(hist1, hist1).flatten()
    hist2 = cv2.normalize(hist2, hist2).flatten()
    
    # 计算相似度(相关系数)
    similarity = cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
    return similarity

3.2 三通道直方图

分别计算 RGB 三个通道的直方图,综合比较:

python
def compare_color_histogram(img1_path, img2_path):
    img1 = cv2.imread(img1_path)
    img2 = cv2.imread(img2_path)
    
    # 分别计算三个通道
    similarity_total = 0
    for i in range(3):
        hist1 = cv2.calcHist([img1], [i], None, [256], [0, 256])
        hist2 = cv2.calcHist([img2], [i], None, [256], [0, 256])
        
        hist1 = cv2.normalize(hist1, hist1).flatten()
        hist2 = cv2.normalize(hist2, hist2).flatten()
        
        similarity_total += cv2.compareHist(hist1, hist2, cv2.HISTCMP_CORREL)
    
    return similarity_total / 3

4. 特征提取方法

4.1 SIFT 特征

SIFT(Scale-Invariant Feature Transform,尺度不变特征变换)是经典的特征提取算法:

python
import cv2

def sift_similarity(img1_path, img2_path):
    img1 = cv2.imread(img1_path, cv2.IMREAD_GRAYSCALE)
    img2 = cv2.imread(img2_path, cv2.IMREAD_GRAYSCALE)
    
    # 创建 SIFT 检测器
    sift = cv2.SIFT_create()
    
    # 检测关键点和描述符
    kp1, des1 = sift.detectAndCompute(img1, None)
    kp2, des2 = sift.detectAndCompute(img2, None)
    
    # 使用 FLANN 匹配器
    FLANN_INDEX_KDTREE = 1
    index_params = dict(algorithm=FLANN_INDEX_KDTREE, trees=5)
    search_params = dict(checks=50)
    flann = cv2.FlannBasedMatcher(index_params, search_params)
    
    matches = flann.knnMatch(des1, des2, k=2)
    
    # 应用比率测试
    good_matches = []
    for m, n in matches:
        if m.distance < 0.7 * n.distance:
            good_matches.append(m)
    
    return len(good_matches) / max(len(kp1), len(kp2))

5. 深度学习嵌入方法

5.1 使用 CLIP 模型

CLIP(Contrastive Language-Image Pre-Training)是 OpenAI 开发的多模态模型:

python
from PIL import Image
import torch
from transformers import CLIPProcessor, CLIPModel

# 加载模型
model = CLIPModel.from_pretrained("openai/clip-vit-large-patch14")
processor = CLIPProcessor.from_pretrained("openai/clip-vit-large-patch14")

def get_image_embedding(image_path):
    image = Image.open(image_path)
    inputs = processor(images=image, return_tensors="pt")
    with torch.no_grad():
        image_features = model.get_image_features(**inputs)
    return image_features

def cosine_similarity(emb1, emb2):
    return torch.nn.functional.cosine_similarity(emb1, emb2).item()

# 使用示例
emb1 = get_image_embedding('image1.jpg')
emb2 = get_image_embedding('image2.jpg')
similarity = cosine_similarity(emb1, emb2)

5.2 常用骨干网络

不同的骨干网络适用于不同场景:

模型特点适用场景
简单 CNN轻量快速基础嵌入
MobileNetV3移动端优化移动应用
VGG16经典骨干网络通用场景
ResNet残差网络,深层训练稳定高精度要求
ViTTransformer 架构大规模数据
CLIP多模态预训练通用图像理解
SWAV无监督对比聚类无标注数据
EfficientNet效率优化平衡性能和速度

5.3 第三方库推荐

imagededup

功能丰富的图片去重库,支持多种算法:

bash
pip install imagededup
python
from imagededup.methods import PHash

phasher = PHash()
encodings = phasher.encode_images(image_dir='path/to/images')
duplicates = phasher.find_duplicates(encoding_map=encodings)

官方文档:https://idealo.github.io/imagededup/

imgbeddings

基于深度学习的图片嵌入库:

bash
pip install imgbeddings
python
from imgbeddings import imgbeddings
from PIL import Image

ibed = imgbeddings()
img = Image.open('image.jpg')
embedding = ibed.to_embeddings(img)

GitHub:https://github.com/minimaxir/imgbeddings

6. 方法对比与选择

6.1 性能对比

方法速度精度内存占用适用场景
哈希算法极快中等极小大规模去重、快速筛选
直方图颜色相似度比较
SIFT/SURF局部特征匹配、物体识别
深度学习嵌入极高高精度要求、语义相似度

6.2 选择建议

  1. 大规模图片去重:优先使用哈希算法(pHash 推荐)
  2. 高精度相似度:使用深度学习方法(CLIP、ViT)
  3. 实时应用:使用轻量级哈希或 MobileNet
  4. 离线批处理:可以使用更复杂的深度学习模型
  5. 移动端应用:使用 MobileNet 或哈希算法

7. 参考资料