找回密码
 立即注册
搜索

python图像辨认之图片相似度计算



作者 | a1131825850疯子

来源 | Python爬虫scrapy

1.背景

要辨认两张图片能否相似,首先我们能够会区分这两张图是人物照,还是风景照等......对应的风景照是蓝天还是大海......做一系列的分类。

从机器学习的的角度来说,首先要提取图片的特征,将这些特征停止分类处理,训练并建立模型,然后在停止辨认。

但是让计算机去区分这些图片分别是哪一类是很不容易的,不过计算机可以知道图像的像素值的,因此,在图像辨认过程中,经过颜色特征来辨认是相似图片是我们常用的(当然还有其特征还有纹理特征、外形特征和空间关系特征等,这些有分为直方图,颜色集,颜色局,聚合向量,相关图等来计算颜色特征),

为了得到两张相似的图片,在这里经过以下几种简单的计算方式来计算图片的相似度:

直方图计算图片的相似度

经过哈希值,汉明间隔计算

经过图片的余弦间隔计算

经过图片结构度量计算

一、直方图计算图片的相似度

上三张图片,分别是img1.png, img2.jpg,img.png:



可以看出下面这三张图是挺相似的,在颜色上是差不多的,最相似的是哪两张大家可以猜猜看,看和我们计算的能否一样。

在python中应用opencv中的calcHist()方法获取其直方图数据,前往的结果是一个列表:
# 计算图img1的直方图H1 = cv2.calcHist([img1], [1], None, [256], [0, 256])H1 = cv2.normalize(H1, H1, 0, 1, cv2.NORM_MINMAX, -1) # 对图片停止归一化处理
先计算img1的直方图,在对其归一化,最后在分别对img2,img3计算,做归一化,然后在应用python自带的compareHist()停止相似度的比较:
应用compareHist()停止比较相似度similarity1 = cv2.compareHist(H1, H2, 0)
最后得到三张图片的直方图如下:

图像的x轴是指的图片的0~255之间的像素变化,y轴指的是在这0~255像素所占的比列。

我们可以分明的看出img2与img3的直方图的变化趋向是相符的有重合态的,运转结果如下:

经过运转结果知道img2和img3是值是最为相似的(代码calcImage.py)

下面的是直接调用opencv中的方法来完成的,下面还有本人写的方法:

首先是将图片转化为RGB格式,在这里是用的pillow中的Image来对图片做处理的:
# 将图片转化为RGBdef make_regalur_image(img, size=(64, 64)): gray_image = img.resize(size).convert('RGB') return gray_image
在计算两图片的直方图:
# 计算直方图def hist_similar(lh, rh): assert len(lh) == len(rh) hist = sum(1 - (0 if l == r else float(abs(l - r)) / max(l, r)) for l, r in zip(lh, rh)) / len(lh) return hist
在计算其相似度:
# 计算相似度def calc_similar(li, ri): calc_sim = hist_similar(li.histogram(), ri.histogram())returncalc_sim
得到最终的运转结果:

两种方法的的结果还是有点差距的,可以看到img1和img3的结果相似度高些。

不过两者的相似度计算方法如下:

gi和si分别指的是两条曲线的第i个点。

总结:

应用直方图计算图片的相似度时,是按照颜色的全局分布状况来对待的,无法对部分的颜色停止分析,同一张图片假如转化成为灰度图时,在计算其直方图时差距就更大了。

为了处理这个成绩,可以将图片停止等分,然后在计算图片的相似度。不过在这里我就不叙说了,大家自行讨论!!!

二、哈希算法计算图片的相似度

在计算之前我们先了解一下图像指纹和汉明间隔:

图像指纹:

图像指纹和人的指纹一样,是身份的意味,而图像指纹简单点来讲,就是将图像按照一定的哈希算法,经过运算后得出的一组二进制数字。

汉明间隔:

假如一组二进制数据为101,另外一组为111,那么显然把第一组的第二位数据0改成1就可以变成第二组数据111,所以两组数据的汉明间隔就为1。简单点说,汉明间隔就是一组二进制数据变成另一组数据所需的步骤数,显然,这个数值可以衡量两张图片的差异,汉明间隔越小,则代表相似度越高。汉明间隔为0,即代表两张图片完全一样。

感知哈希算法是一类算法的总称,包括aHash、pHash、dHash。顾名思义,感知哈希不是以严厉的方式计算Hash值,而是以愈加相对的方式计算哈希值,由于“相似”与否,就是一种相对的断定。

几种hash值的比较:

aHash:平均值哈希。速度比较快,但是常常不太准确。

pHash:感知哈希。准确度比较高,但是速度方面较差一些。

dHash:差异值哈希。准确度较高,且速度也非常快

1. 平均哈希算法(aHash):

该算法是基于比较灰度图每个像素与平均值来完成。

aHash的hanming间隔步骤:

先将图片紧缩成8*8的小图

将图片转化为灰度图

计算图片的Hash值,这里的hash值是64位,或者是32位01字符串

将下面的hash值转换为16位的

经过hash值来计算汉明间隔
# 均值哈希算法def ahash(image): # 将图片缩放为8*8的 image = cv2.resize(image, (8, 8), interpolation=cv2.INTER_CUBIC) # 将图片转化为灰度图 gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY) # s为像素和初始灰度值,hash_str为哈希值初始值 s = 0 # 遍历像素累加和 for i in range(8): for j in range(8): s = s + gray[i, j] # 计算像素平均值 avg = s / 64 # 灰度大于平均值为1相反为0,得到图片的平均哈希值,此时得到的hash值为64位的01字符串 ahash_str = '' for i in range(8): for j in range(8): if gray[i, j] > avg: ahash_str = ahash_str + '1' else: ahash_str = ahash_str + '0' result = '' for i in range(0, 64, 4): result += ''.join('%x' % int(ahash_str[i: i + 4], 2)) # print("ahash值:",result) return result
2.感知哈希算法(pHash):

均值哈希虽然简单,但是受均值影响大。假如对图像停止伽马校正或者停止直方图均值化都会影响均值,从而影响哈希值的计算。所以就有人提出更健壮的方法,经过团圆余弦(DCT)停止低频提取。

团圆余弦变换(DCT)是种图像紧缩算法,它将图像从像素域变换到频率域。然后普通图像都存在很多冗余和相关性的,所以转换到频率域之后,只要很少的一部分频率分量的系数才不为0,大部分系数都为0(或者说接近于0)。

pHash的计算步骤:

减少图片:32 * 32是一个较好的大小,这样方便DCT计算转化为灰度图

计算DCT:应用Opencv中提供的dct()方法,留意输入的图像必须是32位浮点型,所以先应用numpy中的float32停止转换

减少DCT:DCT计算后的矩阵是32 * 32,保留左上角的8 * 8,这些代表的图片的最低频率

计算平均值:计算减少DCT后的一切像素点的平均值。

进一步减小DCT:大于平均值记录为1,反之记录为0.

得到信息指纹:组合64个信息位,顺序随意保持分歧性。

最后比对两张图片的指纹,获得汉明间隔即可。
# phashdef phash(path): # 加载并调整图片为32*32的灰度图片 img = cv2.imread(path) img1 = cv2.resize(img, (32, 32),cv2.COLOR_RGB2GRAY) # 创建二维列表 h, w = img.shape[:2] vis0 = np.zeros((h, w), np.float32) vis0[:h, :w] = img1 # DCT二维变换 # 团圆余弦变换,得到dct系数矩阵 img_dct = cv2.dct(cv2.dct(vis0)) img_dct.resize(8,8) # 把list变成一维list img_list = np.array().flatten(img_dct.tolist()) # 计算均值 img_mean = cv2.mean(img_list) avg_list = ['0' if i gray[i, j + 1]: dhash_str = dhash_str + '1' else: dhash_str = dhash_str + '0' result = '' for i in range(0, 64, 4): result += ''.join('%x' % int(dhash_str[i: i + 4], 2)) # print("dhash值",result)returnresult
4. 计算哈希值差异
#计算两个哈希值之间的差异def campHash(hash1, hash2): n = 0 # hash长度不同前往-1,此时不能比较 if len(hash1) != len(hash2): return -1 # 假如hash长度相反遍历长度 for i in range(len(hash1)): if hash1 != hash2: n = n + 1 return n
最终的运转结果:

aHash:

dhash:

p_hsah:

经过下面运转的结果可以看出来,img1和img2的相似度高一些。

三、余弦相似度(cosin)

把图片表示成一个向量,经过计算向量之间的余弦间隔来表征两张图片的相似度。

1. 对图片停止归一化处理
# 对图片停止一致化处理def get_thum(image, size=(64, 64), greyscale=False): # 应用image对图像大小重新设置, Image.ANTIALIAS为高质量的 image = image.resize(size, Image.ANTIALIAS) if greyscale: # 将图片转换为L形式,其为灰度图,其每个像素用8个bit表示 image = image.convert('L') return image
2. 计算余弦间隔
# 计算图片的余弦间隔def image_similarity_vectors_via_numpy(image1, image2): image1 = get_thum(image1) image2 = get_thum(image2) images = [image1, image2] vectors = [] norms = [] for image in images: vector = [] for pixel_tuple in image.getdata(): vector.append(average(pixel_tuple)) vectors.append(vector) # linalg=linear(线性)+algebra(代数),norm则表示范数 # 求图片的范数?? norms.append(linalg.norm(vector, 2)) a, b = vectors a_norm, b_norm = norms # dot前往的是点积,对二维数组(矩阵)停止计算 res = dot(a / a_norm, b / b_norm)returnres
最终运转结果:

结果显示img1和img2的相似度高一些,和计算hash值的汉明间隔得到的结果是相分歧的。

四、图片SSIM(结构相似度量)

SSIM是一种全参考的图像质量评价目的,分别从亮度、对比度、结构三个方面度量图像相似性。SSIM取值范围[0, 1],值越大,表示图像失真越小。在实践运用中,可以应用滑动窗将图像分块,令分块总数为N,思索到窗口外形对分块的影响,采用高斯加权计算每一窗口的均值、方差以及协方差,然后计算对应块的结构相似度SSIM,最后将平均值作为两图像的结构相似性度量,即平均结构相似性SSIM。
ssim1 = compare_ssim(img1, img2, multichannel=True)
这个是scikit-image库自带的一种计算方法

运转结果:

可以看到img1和img2的相似度高。

好了,以上就是到目前为止我接触到的图片相似度的计算方法,一定还有许多我没有接触到的计算方法,大家有需求的可以参考一下,有其他方法的大家可以留言一同讨论!!!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

大神点评5

左岸永年 2019-11-20 18:49:25 显示全部楼层
分享了
回复

使用道具 举报

abc5441 2019-11-20 18:53:10 显示全部楼层
分享了
回复

使用道具 举报

duckey_020 2019-11-21 16:26:52 显示全部楼层
专业抢沙发的!哈哈
回复

使用道具 举报

极品月光猪 2019-11-22 14:37:39 显示全部楼层
前排支持下了哦~
回复

使用道具 举报

kelong520 2019-11-23 13:12:56 显示全部楼层
边撸边过
回复

使用道具 举报

高级模式
B Color Image Link Quote Code Smilies