图像相似度方法调研
date: 2022-03-22
一、测试数据
- 五组相互对应相似图片
- 五组相互部分相似图片
- 五组相互不相似图片
二、可用算法
算法名称 | 计算距离方法 | 度量规则 | 优缺点 |
---|---|---|---|
感知哈希(pHash) | 汉明距离 | [0,10]汉明距离越小图片越相似 | 效率较低,但误报率在哈希方法中最低 |
差异哈希(dHash) | 汉明距离 | [0,10]汉明距离越小图片越相似 | 效率较高,误报率比phash方法高 |
余弦距离(cosin) | 余弦距离 | [0,1]图片越相似,余弦值越接近1 | 算法简单,效率较慢,准确率不错 |
结构相似度(ssim) | ssim | [0,1]图片越相似,值越接近1 | 大多用于考量同一张图片的失真率 |
三、算法步骤
-
感知哈希(pHash)
1.缩放图片:图片缩放为32*32,利于DCT(离散余弦变换)计算;
2.灰化:将图片转化为256阶灰度图;
3.计算DCT:DCT把图片分离成分率的集合;
4.缩小DCT:DCT计算后的矩阵是3 *32,保留左上角的8 * 8,代表图片最低频率;
5.计算平均值:计算缩小DCT后的所有像素点的平均值;
6.比较平均值:大于平均值记录为1,反之记录为0,得到phash值;
-
差异哈希(dHash)
1.图片缩放为9*8,保留结构;
2.灰化:将图片转化为256阶灰度图;
3.求平均值:计算灰度图所有像素的平均值;
4.比较:像素值大于后一个像素记作1,相反记作0;
5.生成哈希值;
-
余弦距离(cosin)
1.图片处理:将图片设置为统一大小如64*64
2.灰化:将图片转化为灰度图
3.计算图片的余弦相似度
-
结构相似度(ssim)
1.图片滑窗:将图形分块,方块数为N;
2.计算计算窗口均值、方差以及协方差;
3.计算对应块的结构相似度;
4.计算全图结构相似度:将对应块结构相似度求平均值;
四、算法结果比较
- 相似图片对比结果
算法名称 | 度量规则 | 结果 | 运行时间 |
---|---|---|---|
感知哈希(pHash) | [0,10]汉明距离越小图片越相似 | [0.87, 0.90, 0.96, 0.90, 0.81] | 0.9s |
差异哈希(dHash) | [0,10]汉明距离越小图片越相似 | [0.78, 0.93, 0.96, 0.95, 0.85] | 0.9s |
余弦距离(cosin) | [0,1]图片越相似,余弦值越接近1 | [0.9,0.87,0.85,0.94,0.76 ] | 1.2s |
结构相似度(ssim) | [0,1]图片越相似,值越接近1 | [0.68, 0.50, 0.69, 0.50, 0.57] | 1.4s |
- 部分相似图片对比结果
算法名称 | 度量规则 | 结果 | 运行时间 |
---|---|---|---|
感知哈希(pHash) | [0,10]汉明距离越小图片越相似 | [0.81, 0.781, 0.85, 0.56, 0.53] | 0.6s |
差异哈希(dHash) | [0,10]汉明距离越小图片越相似 | [0.82, 0.84, 0.85, 0.625, 0.51] | 0.1s |
余弦距离(cosin) | [0,1]图片越相似,余弦值越接近1 | [0.675,0.737,0.685,0.537,0.76 ] | 0.7s |
结构相似度(ssim) | [0,1]图片越相似,值越接近1 | [0.34, 0.40, 0.405, 0.45, 0.38] | 0.2s |
- 不相似图片对比结果
算法名称 | 度量规则 | 结果 | 运行时间 |
---|---|---|---|
感知哈希(pHash) | [0,10]汉明距离越小图片越相似 | [0.562, 0.531, 0.562, 0.468, 0.468] | 0.6 s |
差异哈希(dHash) | [0,10]汉明距离越小图片越相似 | [0.43, 0.45, 0.57, 0.59, 0.546] | 0.2 s |
余弦距离(cosin) | [0,1]图片越相似,余弦值越接近1 | [0.412,0.327,0.57,0.317,0.488 ] | 0.7 s |
结构相似度(ssim) | [0,1]图片越相似,值越接近1 | [0.23, 0.52, 0.53, 0.32, 0.44] | 0.6s |
五、算法代码示例
python代码示例:
###读取文件名
def file_name(file_dir):
file_name = []
file_dan = []
file_shuang = []
for root,dirs,files in os.walk(file_dir):
file_name.append(files)
file_dan.append(file_name[1])
file_shuang.append(file_name[2])
return file_dan[0],file_shuang[0]
def image_similar_dhash(file_dir):
'''
dhash
'''
start = datetime.datetime.now()
list_1,list_2 = file_name(file_dir)
hash_dan = []
hash_shuang = []
for i in list_1:
figuredan_path = file_dir + 'one/'+i
hash1 = dhash(Image.open(figuredan_path))
hash_dan.append(hash1)
for j in list_2:
figures_path = file_dir +'two/' + j
hash2 = dhash(Image.open(figures_path))
hash_shuang.append(hash2)
image_similar_dhash = list(map(lambda x : 1 - (x[0] - x[1]) / len(x[0].hash) ** 2,zip(hash_dan,hash_shuang)))
end = datetime.datetime.now()
run_time = end - start
return image_similar_dhash,run_time
def image_similar_phash(file_dir):
'''
dhash
'''
start = datetime.datetime.now()
list_1,list_2 = file_name(file_dir)
hash_dan = []
hash_shuang = []
for i in list_1:
figuredan_path = file_dir + 'one/'+i
hash1 = phash(Image.open(figuredan_path))
hash_dan.append(hash1)
for j in list_2:
figures_path = file_dir +'two/' + j
hash2 = phash(Image.open(figures_path))
hash_shuang.append(hash2)
image_similar_phash = list(map(lambda x : 1 - (x[0] - x[1]) / len(x[0].hash) ** 2,zip(hash_dan,hash_shuang)))
end = datetime.datetime.now()
run_time = end - start
return image_similar_phash,run_time
def get_thum(img,size=(64,64),greyscale = False):
'''
图片变灰度
'''
im = Image.open(img)
image = im.resize(size,Image.ANTIALIAS)
if greyscale:
image = image.convert('L')
return image
def image_similar_cosin(file_dir):
list_1,list_2 = file_name(file_dir)
image_dan = []
image_shuang = []
result = []
for i in list_1:
figuredan_path = file_dir + 'one/'+ i
image1 = get_thum(figuredan_path)
image_dan.append(image1)
for j in list_2:
figureshuang_path = file_dir + 'two/'+ j
image2 = get_thum(figureshuang_path)
image_shuang.append(image2)
image_list = list(map(lambda x : [x[0],x[1]] ,zip(image_dan,image_shuang)))
vectors = []
norms = []
for q in image_list[0]:###进入每个list[figure1,fiure2]\
for pixel_tuple in q.getdata():
vector = []
vector.append(average(pixel_tuple))
vectors.append(vector)
norms.append(linalg.norm(vector,2))
a,b = vectors
a_norm,b_norm = norms
result = dot(a / a_norm , b / b_norm)
return result
###ssim
import cv2
from skimage.measure import compare_ssim
def image_similar_ssim(file_dir):
list_1,list_2 = file_name(file_dir)
image_dan = []
image_shuang = []
shape_dan = []
for i in list_1:
figuredan_path = file_dir + 'one/'+ i
image1 = cv2.imread(figuredan_path)
image_dan.append(image1)
for j in image_dan:
shape_dan.append((j.shape[1],j.shape[0]))
for q in list_2:
figures_path = file_dir + 'two/' + q
image2 = cv2.imread(figures_path)
image_shuang.append(image2)
v = list(map(lambda x : cv2.resize(x[0],x[1]),zip(image_shuang,shape_dan)))
res = list(map(lambda x : compare_ssim(x[0],x[1],full =False,multichannel=True),zip(image_dan,v) ))
return res
扩展:基于直方图方法及PSNR方法
-
基于直方图方法:能描述一副图像中的颜色全局分布,但过于简单,捕捉不到更多的信息,只要颜色分布相似就判断为两张图片相似度较高。
相似图片相似度:0.68 运行时间:0.4s;
部分相似图片相似度:0.65 运行时间:0.5s;
不相似图片相似度:0.52 运行时间:0.7;
-
PSNR:峰值信噪比,基于对应像素点间误差的图像评价指标,但未考虑人眼对空间频率较低的对比差异敏感度较高的特性,且该方法多用于评价一张图片的失真情况。PSNR接近 50dB ,代表压缩后的图像仅有些许非常小的误差。PSNR介于 10dB 到 20dB 之间,人眼还是可以用肉眼看出这个图像原始的结构,且直观上会判断两张图像不存在很大的差异。
相似图片相似度:17.38d 运行时间:0.5s
部分相似图片相似度:12.49d 运行时间:0.5s
不相似图片相似度:10.39d 运行时间:0.1s
六、总结
上述四种方法中,推荐使用差异哈希(dHash)方法,该方法在保证推理速度较高的同时能保证误报率低。