OpenCV实战之二 | 基于哈希算法比较图像的相似性

前言
☘️ 本章节主要介绍常用的图像相似性评价算法:图像哈希算法。图像哈希算法通过获取图像的哈希值并比较两幅图像的哈希值的汉明距离来衡量两幅图像是否相似。两幅图像越相似,其哈希值的汉明距离越小。图像哈希算法可以用于图片检索,重复图片剔除,以图搜图以及图片相似度比较。

目录

  • 一、汉明距离
  • 二、img_hash模块
  • 三、哈希算法
    • 哈希算法实现步骤:
    • 代码实现

一、汉明距离

汉明距离(Hamming Distance)是用于比较两个等长字符串或二进制序列的差异度的度量。它表示两个字符串在相同位置上不相同的字符或位的数量。汉明距离的值越小,表示两个字符串越相似;而值越大,表示两个字符串越不相似。
简单举个例子,假设有两个等长的二进制序列:

  • 序列1: 1010101
  • 序列2: 1001001

要计算这两个序列的汉明距离,可以按照以下步骤进行:

  1. 将两个序列逐位进行比较。
  2. 在相同位置上,如果两个序列的位不相同,则将汉明距离加1。
  3. 在相同位置上,如果两个序列的位相同,则不增加汉明距离。

在上述示例中,我们可以看到序列1和序列2的差异在第2位和第6位上,所以它们的汉明距离为2。

二、img_hash模块

  • OpenCV的img_hash模块提供了多种图像哈希算法,具体介绍如下:

    • Average hash平均哈希,基于像素均值计算哈希值,速度比较快,但是常常不太精确,仅适用于简单情况。
    • PHash感知哈希(Perceptual Hash),使用DCT(离散余弦变换)来提取图像的低频分量,然后根据这些分量计算哈希值。精确度比较高,但是速度方面较差一些。
    • Marr Hildreth Hash:基于Marr-Hildreth边缘算子计算哈希值,速度最慢,但更具区分性。
    • Radial Variance Hash:基于Radon变换计算哈希值
    • Block Mean Hash:基于块均值计算哈希值
    • Color Moment Hash 基于颜色矩计算哈希值
  • 实现代码如下:

    def get_hash(mode, a, b):
        if mode == "AverageHash":
            hashFun = cv2.img_hash.AverageHash_create()
        elif mode == "PHash":
            hashFun = cv2.img_hash.PHash_create()
        elif mode == "MarrHildrethHash":
            hashFun = cv2.img_hash.MarrHildrethHash_create()
        elif mode == "RadialVarianceHash":
            hashFun = cv2.img_hash.RadialVarianceHash_create()
        elif mode == "BlockMeanHash":
            hashFun = cv2.img_hash.BlockMeanHash_create()
        elif mode == "ColorMomentHash":
            hashFun = cv2.img_hash.ColorMomentHash_create()
        else:
            # 默认使用感知哈希算法
            hashFun = cv2.img_hash.PHash_create()
    
        tick = cv2.TickMeter()
        print("=== " + mode + " ===")
    
        tick.reset()
        tick.start()
        # # 计算图a的哈希值
        hashA = hashFun.compute(a)
        tick.stop()
        print("compute1: " + str(tick.getTimeMilli()) + " ms")
    
        tick.reset()
        tick.start()
        # 计算图b的哈希值
        hashB = hashFun.compute(b)
        tick.stop()
        print("compute2: " + str(tick.getTimeMilli()) + " ms")
        # 比较两张图像哈希值的距离
        print("compare: " + str(hashFun.compare(hashA, hashB)))
    

三、哈希算法

  • aHash:平均值哈希。速度比较快,但是常常不太精确。
  • pHash:感知哈希。精确度比较高,但是速度方面较差一些。
  • dHash:差异值哈希。精确度较高,且速度也非常快。

哈希算法实现步骤:

  1. 缩小尺寸:将图像缩小到8*8的尺寸,总共64个像素。这一步的作用是去除图像的细节,只保留结构/明暗等基本信息,摒弃不同尺寸/比例带来的图像差异;
  2. 将图片转化为灰度图
  3. 比较像素的灰度:如果前一个像素的颜色强度大于第二个像素,那么差异值就设置为True(也就是1),如果不大于第二个像素,就设置为False(也就是0)
  4. 计算hash值:将差异值数组中每一个值看做一个bit,每8个bit组成为一个16进制值,将16进制值连接起来转换为字符串,就得出了最后的dHash值;
  5. 计算汉明距离

代码实现

因aHash和pHash代码可通过img_hash模块实现,所以这里只对dHash进行代码实现,也可参考这个代码:

import cv2
import numpy as np


def dHash(img):
    # 将图片转化为8*8
    img = cv2.resize(img, (9, 8), interpolation=cv2.INTER_CUBIC)
    # 将图片转化为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # 每行前一个像素大于后一个像素为1,相反为0,生成哈希
    hash_str0 = []
    for i in range(8):
        hash_str0.append(gray[:, i] > gray[:, i + 1])
    hash_str1 = np.array(hash_str0) + 0
    hash_str2 = hash_str1.T
    hash_str3 = hash_str2.reshape(1, -1)[0].tolist()
    dhash_str = "".join([str(x) for x in hash_str3])
    return dhash_str


def hammingDist(s1, s2):
    assert len(s1) == len(s2)
    return sum([ch1 != ch2 for ch1, ch2 in zip(s1, s2)])


if __name__ == "__main__":
    img1 = cv2.imread("./images/1.jpg")
    img2 = cv2.imread("./images/2.jpg")
    img3 = cv2.imread("./images/3.jpg")
    print(hammingDist(dHash(img1), dHash(img2)))
    print(hammingDist(dHash(img1), dHash(img3)))
    print(hammingDist(dHash(img2), dHash(img3)))

你可能感兴趣的:(OpenCV实战笔记,opencv,哈希算法,人工智能)