图像相似度对比

【声明】度量两张图片的相似度有许多算法,本文将对常用的图片相似度算法进行汇总。部分数据、资料来源于各技术网站,如有侵权烦请联系删除。
常用的算法有几类:

一、Hash算法

Hash算法常用的有三种,分别为平均哈希算法(aHash)、感知哈希算法你(pHash)和差异哈哈希算法(dHash);还有一种是小波哈希算法(whash)。 Hash算法都是通过获取图片的hash值,再比较两张图片hash值的汉明距离来度量两张图片是否相似。两张图片越相似,那么两张图片的hash数的汉明距离越小。

1、aHash-平均哈希

算法步骤

平均哈希算法是三种Hash算法中最简单的一种,它通过下面几个步骤来获得图片的Hash值:

(1) 缩放图片;
(2) 转灰度图; 
(3) 算像素均值;
(4) 根据相似均值计算指纹.

image.png
得到图片的ahash值后,比较两张图片ahash值的汉明距离,通常认为汉明距离小于10的一组图片为相似图片。

举栗

我们以一张动物的图片为例,详解ahash算法过程:
原图:
husky.jpg

缩放图片
先将图片缩放为8*8的图片,得到下图:
image.png

图片灰度
得到灰度图:
image.png
得到单通道灰度图的各个像素值
image.png

计算平均值
求得的平均值为152.4375

每个像素点与平均值比较
image.png
那么该图片的ahash值即为:1111100011110000111111001111011011100111111011101111000000001000

ahash代码

import cv2
import numpy as np
import copy


def ahash_process(pic_path):
    '''
    ahash算法过程
    :param pic_path: 图片路径
    :return: 
    '''
    img = cv2.imread(pic_path, cv2.IMREAD_UNCHANGED)
    img_resize = cv2.resize(img, (8, 8), cv2.INTER_AREA)
    gray_img = cv2.cvtColor(img_resize, cv2.COLOR_BGR2GRAY)
    print('单通道灰度图:', gray_img)

    avg = np.mean(gray_img)
    print('平均值:', avg)

    dis = copy.deepcopy(gray_img)
    dis_hash_str = ''
    for x_index, x in enumerate(gray_img):
        for y_index, y in enumerate(x):
            if y >= avg:
                dis[x_index, y_index] = 1
            else:
                dis[x_index, y_index] = 0
            dis_hash_str += str(dis[x_index, y_index])
    print('与平均值比较:', dis)
    print('该图片的ahash值:', dis_hash_str)

    cv2.namedWindow('resize', 0)
    cv2.resizeWindow('resize', 600, 600)
    cv2.imshow('resize', img_resize)

    cv2.namedWindow('gray', 0)
    cv2.resizeWindow('gray', 600, 600)
    cv2.imshow('gray', gray_img)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

两张图片对比自然需要另外一张图片,同理通过上述方法,计算出hash值后,计算汉明距离即可。

2、dHash-差异哈希

算法步骤

dhash和ahash算法的差别在于,ahash使用每个像素点与平均像素做对比得出布尔值,dhash是使用当前像素与后一个像素点做对比得到布尔值,正是这个原因,所以dhash算法需要将图片缩放为9*8个像素点。

(1)图片缩放为9*8,保留结构,出去细节; 
(2)灰度化:转换为256阶灰度图; 
(3)求平均值:计算灰度图所有像素的平均值;
(4)比较:像素值大于后一个像素值记作1,相反记作0。本行不与下一行对比,每行9个像素,八个差值,有8行,总共64位 ;
(5)生成hash:将上述步骤生成的1和0按顺序组合起来既是图片的指纹(hash); 

举栗

原图:
husky.jpg

缩放图片
先将图片缩放为9*8的图片,得到下图:
image.png
图片灰度
得到灰度图:
image.png

得到单通道灰度图的各个像素值
image.png

每个像素点与后面一个像素点比较值
image.png

那么该图片的dhash值即为:0011011010111011010110101111001011010011101110011110101111001101

dHash代码

import cv2


def dhash_process(pic_path):
    img = cv2.imread(pic_path, cv2.IMREAD_UNCHANGED)
    img_resize = cv2.resize(img, (9, 8), cv2.INTER_AREA)
    gray_img = cv2.cvtColor(img_resize, cv2.COLOR_BGR2GRAY)
    print('单通道灰度图:', gray_img)

    dis = [[] for x in range(8)]
    dis_hash_str = ''
    for row in range(8):
        for col in range(8):
            if gray_img[row, col] >= gray_img[row, col + 1]:
                dis[row].append(1)
                dis_hash_str += str(1)
            else:
                dis[row].append(0)
                dis_hash_str += str(0)

    print('比较值:', dis)
    print('该图片的dhash值:', dis_hash_str)

    cv2.namedWindow('resize', 0)
    cv2.resizeWindow('resize', 600, 600)
    cv2.imshow('resize', img_resize)

    cv2.namedWindow('gray', 0)
    cv2.resizeWindow('gray', 600, 600)
    cv2.imshow('gray', gray_img)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

3、pHash-感知哈希

算法步骤

dhash是三种Hash算法中较为复杂的一种,它是基于DCT(离散余弦变换)来得到图片的hash值:

(1)缩小图片:32*32是一个较好的大小,这样方便DCT计算;
(2)灰度化:转换为256阶灰度图; 
(3)计算DCT:DCT把图片分离成分率的集合,DCT(离散余弦变换);
(4)缩小DCT:DCT计算后的矩阵是32 * 32,保留左上角的8 * 8,这些代表的图片的最低频率;
(5)计算平均值:计算缩小DCT后的所有像素点的平均值; 
(6)比较平均值:大于平均值记录为1,反之记录为0,得到phash值。

举栗

原图:
husky.jpg

缩放图片
先将图片缩放为32*32的图片,得到下图:
image.png

图片灰度
得到灰度图:
image.png

计算图片DCT
得到DCT图:
image.png

得到图片低频DCT
得到低频DCT图:
image.png
低频DCT值:
image.png
平均值为:97.10013

低频DCT值与平均值比较
得到phash值为:1110011010100100000010001101001100010000110000001001000000010000

pHash代码

import cv2
import numpy as np


def phash_process(pic_path):
    img = cv2.imread(pic_path, cv2.IMREAD_UNCHANGED)
    img_resize = cv2.resize(img, (32, 32), cv2.INTER_AREA)
    gray_img = cv2.cvtColor(img_resize, cv2.COLOR_BGR2GRAY)
    print('单通道灰度图:', gray_img)

    gray_img_dct = cv2.dct(np.float32(gray_img))
    gray_img_low_dct = gray_img_dct[0:8, 0:8]
    print('低频DCT图值:', gray_img_low_dct)

    avg = np.mean(gray_img_low_dct)
    print('低频DCT图平均值:', avg)

    dis = [[] for x in range(8)]
    dis_hash_str = ''

    for row_index, row in enumerate(gray_img_low_dct):
        for col_index, col in enumerate(row):
            print(row_index, col_index)
            if col >= avg:
                dis[row_index].append(1)
                dis_hash_str += '1'
            else:
                dis[row_index].append(0)
                dis_hash_str += '0'

    print('phash值:', dis_hash_str)

    cv2.namedWindow('resize', 0)
    cv2.resizeWindow('resize', 600, 600)
    cv2.imshow('resize', img_resize)

    cv2.namedWindow('gray', 0)
    cv2.resizeWindow('gray', 600, 600)
    cv2.imshow('gray', gray_img)

    cv2.namedWindow('DCT', 0)
    cv2.resizeWindow('DCT', 600, 600)
    cv2.imshow('DCT', gray_img_dct)

    cv2.namedWindow('low_DCT', 0)
    cv2.resizeWindow('low_DCT', 600, 600)
    cv2.imshow('low_DCT', gray_img_low_dct)

    cv2.waitKey(0)
    cv2.destroyAllWindows()

二、SSIM算法

SSIM(结构相似性度量),这是一种全参考的图像质量评价指标,分别从亮度、对比度、结构三个方面度量图像相似性。

参考文章:

https://cloud.tencent.com/dev...
https://www.cnblogs.com/Kalaf...
https://blog.csdn.net/u010977...