python图像识别之图片类似度计算

做者 | a1131825850疯子 
来源 | Python爬虫scrapy

1.背景python

要识别两张图片是否类似,首先咱们可能会区分这两张图是人物照,仍是风景照等......对应的风景照是蓝天仍是大海......作一系列的分类。算法

从机器学习的的角度来讲,首先要提取图片的特征,将这些特征进行分类处理,训练并创建模型,而后在进行识别。数组

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

为了获得两张类似的图片,在这里经过如下几种简单的计算方式来计算图片的类似度:机器学习

直方图计算图片的类似度scrapy

经过哈希值,汉明距离计算学习

经过图片的余弦距离计算spa

经过图片结构度量计算3d

1、直方图计算图片的类似度code

上三张图片,分别是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个点。

总结:

利用直方图计算图片的类似度时,是按照颜色的全局分布状况来看待的,没法对局部的色彩进行分析,同一张图片若是转化成为灰度图时,在计算其直方图时差距就更大了。

为了解决这个问题,能够将图片进行等分,而后在计算图片的类似度。不过在这里我就不叙述了,你们自行探讨!!!

2、哈希算法计算图片的类似度

在计算以前咱们先了解一下图像指纹和汉明距离:

图像指纹:

图像指纹和人的指纹同样,是身份的象征,而图像指纹简单点来说,就是将图像按照必定的哈希算法,通过运算后得出的一组二进制数字。

汉明距离:

假如一组二进制数据为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<img_mean else '1' for i in img_list]    return ''.join(['%x' % int(''.join(avg_list[x:x+4]),2) for x in range(0,64,4)])

3. 差别值哈希算法(dHash):

相比pHash,dHash的速度要快的多,相比aHash,dHash在效率几乎相同的状况下的效果要更好,它是基于渐变实现的。

dHash的hanming距离步骤:

先将图片压缩成9*8的小图,有72个像素点

将图片转化为灰度图

计算差别值:dHash算法工做在相邻像素之间,这样每行9个像素之间产生了8个不一样的差别,一共8行,则产生了64个差别值,或者是32位01字符串。

得到指纹:若是左边的像素比右边的更亮,则记录为1,不然为0.

经过hash值来计算汉明距离

# 差别值哈希算法def dhash(image):    # 将图片转化为8*8    image = cv2.resize(image, (9, 8), interpolation=cv2.INTER_CUBIC)    # 将图片转化为灰度图    gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)    dhash_str = ''    for i in range(8):        for j in range(8):            if gray[i, j] > 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[i] != hash2[i]:            n = n + 1    return n

最终的运行结果:

aHash:

dhash:

p_hsah:

经过上面运行的结果能够看出来,img1和img2的类似度高一些。

3、余弦类似度(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值的汉明距离获得的结果是相一致的。

4、图片SSIM(结构类似度量)

SSIM是一种全参考的图像质量评价指标,分别从亮度、对比度、结构三个方面度量图像类似性。SSIM取值范围[0, 1],值越大,表示图像失真越小。在实际应用中,能够利用滑动窗将图像分块,令分块总数为N,考虑到窗口形状对分块的影响,采用高斯加权计算每一窗口的均值、方差以及协方差,而后计算对应块的结构类似度SSIM,最后将平均值做为两图像的结构类似性度量,即平均结构类似性SSIM。

ssim1 = compare_ssim(img1, img2, multichannel=True)

这个是scikit-image库自带的一种计算方法

运行结果:

能够看到img1和img2的类似度高。

好了,以上就是到目前为止我接触到的图片类似度的计算方法,确定还有许多我没有接触到的计算方法,你们有须要的能够参考一下,有其余方法的你们能够留言一块儿探讨!!!

相关文章
相关标签/搜索