图像相似度对比-直方图

想要排除神经网络训练集中大量的重复图片,但网上找不到合适的图片对比工具。自己写一个

要对比的图片

img0img1
请添加图片描述请添加图片描述

1. 直方图相似度对比

如果我们有两张图像,并且这两张图像的直方图一样,或者有极高的相似度,那么在一定程度上,我们可以认为这两幅图是一样的,这就是直方图比较的应用之一

1.1 对比效果

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
import cv2
img0=cv2.imread('./images/diff/0.jpg')
img1=cv2.imread('./images/diff/1.jpg')

#show hist
plt.figure(figsize=(18, 6))
plt.subplot(131)
plt.title('img0')
plt.hist(img0.ravel(), 256, [0, 256])
plt.subplot(132)
plt.title('img1')
plt.hist(img1.ravel(), 256, [0, 256])
plt.show()

cv直方图

1.2 rgb三通道相似度

calcHist()分别计算两张图片的rgb三个通道的直方图,返回的是array数据

  1. imaes:输入的图像
  2. channels:选择图像的通道
  3. mask:掩膜,是一个大小和image一样的np数组,其中把需要处理的部分指定为1,不需要处理的部分指定为0,一般设置为None,表示处理整幅图像
  4. histSize:使用多少个bin(柱子),一般为256
  5. ranges:像素值的范围,一般为[0,255]表示0~255
img0_h_B = cv2.calcHist([img0], [0], None, [256], [0, 255])
img1_h_B = cv2.calcHist([img1], [0], None, [256], [0, 255])
img0_h_G = cv2.calcHist([img0], [1], None, [256], [0, 255])
img1_h_G = cv2.calcHist([img1], [1], None, [256], [0, 255])
img0_h_R = cv2.calcHist([img0], [2], None, [256], [0, 255])
img1_h_R = cv2.calcHist([img1], [2], None, [256], [0, 255])
img0_h_R.shape,img1_h_R.shape
((256, 1), (256, 1))

分别计算三个通道直方图的相似性

similarity_b= cv2.compareHist(img0_h_B, img1_h_B, cv2.HISTCMP_CORREL)## 相关性,越接近1越相关
similarity_g = cv2.compareHist(img0_h_G, img1_h_G, cv2.HISTCMP_CORREL)
similarity_r = cv2.compareHist(img0_h_R, img1_h_R, cv2.HISTCMP_CORREL)
similarity_b,similarity_g,similarity_r
(0.9270454865552646, 0.7014101014407259, 0.02743147899985581)
plt.figure(figsize=(14, 6))
plt.subplot(131)
plt.plot(img0_h_B, 'b')
plt.plot(img1_h_B, 'm--')
plt.subplot(132)
plt.plot(img0_h_G, 'g')
plt.plot(img1_h_G, 'm--')
plt.subplot(133)
plt.plot(img0_h_R, 'r')
plt.plot(img1_h_R, 'm--')
plt.show()

请添加图片描述

从得出的结果,和图像对比图,可知,两张图片b,g通道相似度比较高,红色通道相似度低

直方图相似度对比,只是通过统计两个图像颜色值相似度,如果两个图片明暗度类似,但问题大不相同,也会出现相关性的值接近1。
例如以下图片的相似度都在0.6以上
请添加图片描述

这个时候,可以通过对图片进行切片,通过每次统计一个局部图片的相似度,最后将所有相似度求平均值,排除这种干扰

1.3 对比公式

计算相似度的对比公式类型

. 0——HISTCMP_CORREL 相关性 越接近1表示越像

. 1——HISTCMP_CHISQR 卡方 越接近0表示越像

. 2——HISTCMP_INTERSECT 交集法 数值越大表示越像

. 3——HISTCMP_BHATTACHARYYA(HISTCMP_HELLINGER = HISTCMP_BHATTACHARYYA)
常态分布比对的BHATTACHARYYA距离法 越接近0表示越像

. 4——HISTCMP_CHISQR_ALT

. 5——HISTCMP_KL_DIV

相关性

similarity_b= cv2.compareHist(img0_h_B, img1_h_B, cv2.HISTCMP_CORREL)## 相关性,越接近1越相关
similarity_b
0.9270454865552646

卡方

值越小,相关度越高,最大值无上界,最小值 0。不过好像很难量化,如果返回的是归一化到[0,1]的值会比较好理解

similarity_b= cv2.compareHist(img0_h_B, img1_h_B, cv2.HISTCMP_CHISQR)
similarity_b
5536.208356349769

直方图相交

即求两个直方图相重合的部分,使用这个方法的前提是两个直方图均进行了归一化。结果越接近1,越相似;越接近0,越不相似

img0_h_B_nomal = img0_h_B/255
img1_h_B_nomal = img1_h_B/255
similarity_b= cv2.compareHist(img0_h_B_nomal, img1_h_B_nomal, cv2.HISTCMP_INTERSECT)/255
similarity_b
0.7938485293411741

巴氏距离

similarity_b= cv2.compareHist(img0_h_B, img1_h_B, cv2.HISTCMP_BHATTACHARYYA)
similarity_b
0.09968162729573547

参考:
https://zhuanlan.zhihu.com/p/432041950

2. PIL Image的直方图

2.1 对比图片

两张图片,只有中间区域有稍微差异,显示效果如下

from IPython.display import HTML, display
display(HTML("<table><tr><td><img src='./images/diff/0.jpg', title='img_0'></td><td><img src='./images/diff/1.jpg', title='img_0'></td></tr></table>"))

2.2 直方图统计

histogram()返回图像的直方图。直方图作为像素计数列表返回,源图像中的每个像素值一个,如果是单通道,则每个像素点是256(0-255)的跨度,如果是RGB通道,则是每个像素点是768(0-767)的跨度

img0_path = './images/diff/0.jpg'
img1_path =  './images/diff/1.jpg'
img0 = Image.open(img0_path)
img1 = Image.open(img1_path)
img0,img1
(<PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=230x256 at 0x7F66B953B370>,
 <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=230x256 at 0x7F66BA2A3610>)
len(img0.histogram())
768
2.3 rgb三通道全图直方图
plt.figure(figsize=(14, 8))
plt.subplot(121)
plt.title('img0')
plt.plot(img0.histogram(), 'b--')

plt.subplot(122)
plt.title('img1')
plt.plot(img1.histogram(), 'b--')
plt.show()

请添加图片描述

2.4 rgb各个通道对比图
r_0,g_0,b_0=img0.split()
r_1,g_1,b_1=img1.split()
len(r_0.histogram())#单通道,只有256个长度
256
plt.figure(figsize=(14, 10))
#r通道
plt.subplot(321)
plt.title('img0_r')
plt.plot(r_0.histogram(), 'r--')
plt.subplot(322)
plt.title('img1_r')
plt.plot(r_1.histogram(), 'r--')

plt.subplot(323)
plt.title('img0_g')
plt.plot(g_0.histogram(), 'g--')
plt.subplot(324)
plt.title('img1_g')
plt.plot(g_1.histogram(), 'g--')

plt.subplot(325)
plt.title('img0_g')
plt.plot(b_0.histogram(), 'b--')
plt.subplot(326)
plt.title('img1_b')
plt.plot(b_1.histogram(), 'b--')
plt.show()

请添加图片描述

如上图可以看出,r通道差别比较大,g,b通道几乎一致

2.5 相关性计算

from  PIL import Image

#得到切割的图像,方便更准去的统计直方图相似度
def get_split_img(img, split_num = 10):
    w, h = img.size
    pw, ph = int(w/split_num), int(h/split_num)
    assert w % pw == h % ph == 0
    return [img.crop((i, j, i+pw, j+ph)).copy() for i in range(0, w, pw) \
            for j in range(0, h, ph)]

#图片归一化大小
def noraml_resize(img, size=(500, 500)):
    return img.resize(size).convert('RGB')

def hist_similar_with_cv(lh, rh):
    assert len(lh) == len(rh)
    return cv2.compareHist(np.float32(lh), np.float32(rh), cv2.HISTCMP_CORREL)
#     return sum(1 - (0 if l == r else float(abs(l - r))/max(l, r)) for l, r in zip(lh, rh))/len(lh)


#直接调用opencv的相关性方法
def hist_similar_cv(lh, rh):
    assert len(lh) == len(rh)
    return cv2.compareHist(np.float32(lh), np.float32(rh), cv2.HISTCMP_CORREL)

#直接调用方法实现
def hist_similar(lh, rh):
    assert len(lh) == len(rh)
    split_image_similar_sum = 0
    for l, r in zip(lh, rh):
        if l == r:
            split_image_similar_sum +=0
        else:
            split_image_similar_sum += float(abs(l - r))/max(l, r)
        
    return 1 - split_image_similar_sum/len(lh)

def calc_similar(img0, img1, use_cv = False):
    split_num = 1
    img0_split = get_split_img(img0, split_num=split_num)
    img1_split = get_split_img(img1, split_num=split_num)
    hist_total = 0
    #通过opencv的方法实现
    if use_cv:
        for img0_, img1_ in zip(img0_split, img1_split):
            hist_total += hist_similar_cv(img0_.histogram(), img1_.histogram())
    else:#通过公式实现
        for img0_, img1_ in zip(img0_split, img1_split):
            hist_total += hist_similar(img0_.histogram(), img1_.histogram())        
    return hist_total/(split_num*split_num)

def calc_similar_by_path(img0_path, img1_path, use_cv = False):
    img0, img1 = noraml_resize(Image.open(img0_path)), noraml_resize(Image.open(img1_path))
    return calc_similar(img0, img1, use_cv = use_cv)


similary = calc_similar_by_path(img0_path, img1_path, use_cv = True)
print("两张图片相似度为:cv:%s" % similary)
similary = calc_similar_by_path(img0_path, img1_path, use_cv = False)
print("两张图片相似度为:%s" % similary)
两张图片相似度为:cv:0.6380892028902525
两张图片相似度为:0.763133229375112

完整脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值