• OpenCV #以图搜图:均值哈希算法(Average Hash Algorithm)原理与实验


    1. 介绍

    均值哈希算法(Average Hash Algorithm,简称aHash)哈希算法的一种,主要用来做相似图片的搜索工作。

     

    2. 原理

    均值哈希算法(aHash)首先将原图像缩小成一个固定大小的像素图像,然后将图像转换为灰度图像,通过缩小图像的每个像素与平均灰度值的比较,生成一组哈希值。最后,利用两组图像的哈希值的汉明距离来评估图像的相似度。

    魔法: 概括地讲,均值哈希算法一共可细分六步:

    1. 缩小图像: 将目标图像缩小为一个固定的大小,通常为8x8像素,总共64个像素。作用是去除各种图像尺寸和图像比例的差异,只保留结构、明暗等基本信息,目的是确保图像的一致性,降低计算的复杂度。
    2. 图像灰度化: 将缩小的图像转换为灰度图像。
    3. 灰度平均值: 计算灰度图像的平均灰度值。减少计算量。
    4. 比较平均值: 遍历灰度图像的每个像素,比较每个像素的灰度值是否大于或小于平均值。对于大于等于平均值的像素,将其表示为1,对于小于平均值的像素,将其表示为0。最后,得到一个64位的二进制值(8x8像素的图像)。
    5. 生成哈希值: 由于64位二进制值太长,所以按每4个字符为1组,由2进制转成16进制。这样就转为一个长度为16的字符串。这个字符串也就是这个图像可识别的哈希值,也叫图像指纹,即这个图像所包含的特征。
    6. 哈希值比较: 通过比较两个图像的哈希值的汉明距离(Hamming Distance),就可以评估图像的相似度,距离越小表示图像越相似。

     

    3. 实验

    第一步:缩小图像

    将目标图像缩小为一个固定的大小,通常为8x8像素,总共64个像素。作用是去除各种图像尺寸和图像比例的差异,只保留结构、明暗等基本信息,目的是确保图像的一致性,降低计算的复杂度。

    1)读取原图

    # 测试图片路径
    img_path = 'img_test/apple-01.jpg'
    
    # 通过OpenCV加载图像
    img = cv2.imread(img_path)
    
    # 通道重排,从BGR转换为RGB
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    aHash-0001

    2)缩小原图

    # 缩小图像:使用OpenCV的resize函数将图像缩放为8x8像素,采用Cubic插值方法进行图像重采样
    img_resize = cv2.resize(img, (8, 8), cv2.INTER_CUBIC)
    
    • 1
    • 2

    aHash-0002
    OpenCV 的 cv2.resize() 函数提供了4种插值方法,以根据图像的尺寸变化来进行图像重采样。

    • cv2.INTER_NEAREST: 最近邻插值,也称为最近邻算法。它简单地使用最接近目标像素的原始像素的值。虽然计算速度快,但可能导致图像质量下降。
    • cv2.INTER_LINEAR: 双线性插值,通过对最近的4个像素进行线性加权来估计目标像素的值。比最近邻插值更精确,但计算成本略高。
    • cv2.INTER_CUBIC: 双三次插值,使用16个最近像素的加权平均值来估计目标像素的值。通常情况下,这是一个不错的插值方法,适用于图像缩小。
    • cv2.INTER_LANCZOS4: Lanczos插值,一种高质量的插值方法,使用Lanczos窗口函数。通常用于缩小图像,以保留图像中的细节和纹理。

    第二步:图像灰度化

    将缩小的图像转换为灰度图像。也就是说,所有像素点总共只有64种灰度颜色。

    # 图像灰度化:将彩色图像转换为灰度图像。
    img_gray = cv2.cvtColor(img_resize, cv2.COLOR_BGR2GRAY)
    print(f"缩放8x8的图像中每个像素的颜色=\n{img_gray}")
    
    • 1
    • 2
    • 3

    输出打印:

    缩放8x8的图像中每个像素的颜色=
    [[253 253 253 253 253 253 253 253]
     [253 253 253 148 253 253 253 253]
     [253 253 253 215 178 253 253 253]
     [253 253 119  93 132 176 253 253]
     [253 253  61  61  53 130 253 253]
     [253 253 112  67  66 142 253 253]
     [253 253 252  54  54 253 253 253]
     [253 253 236  63 146 249 253 253]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    aHash-0003

    第三步:灰度平均值

    计算灰度图像的平均灰度值。减少计算量。

    img_average = np.mean(img_gray) 
    print(f"灰度图像中所有像素的平均值={img_average}")
    
    • 1
    • 2

    输出打印:

    灰度图像中所有像素的平均值=209.890625
    
    • 1

    第四步:比较平均值

    遍历灰度图像的每个像素,比较每个像素的灰度值是否大于或小于平均值。对于大于等于平均值的像素,将其表示为1;对于小于平均值的像素,将其表示为0。最后,得到一组长64位的二进制字符串(8x8像素的图像)。因为对于机器而言,只认识0和1,所以这组64位的二进制就可以表示这张图像的结构和亮度分布。

    # 遍历图像像素:嵌套循环遍历图像的所有像素,对比灰度图像的平均灰度值,转换为二进制的图像哈希值
    img_hash_binary = [] 
    for i in range(img_gray.shape[0]): 
        for j in range(img_gray.shape[1]): 
            if img_gray[i,j] >= img_average: 
                img_hash_binary.append(1)
            else: 
                img_hash_binary.append(0)
    print(f"对比灰度图像的平均像素值降噪(图像的二进制哈希值)数组={img_hash_binary}")
    
    # 将列表中的元素转换为字符串并连接起来,形成一组64位的图像二进制哈希值字符串
    img_hash_binary_str = ''.join(map(str, img_hash_binary))
    print(f"对比灰度图像的平均像素值降噪(图像的二进制哈希值)={img_hash_binary_str}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    代码分解和含义如下:

    1. 初始化空列表:创建一个空的列表 img_hash_binary,用于存储图像的哈希值。
    2. 遍历图像像素:嵌套循环遍历图像的所有像素,其中 img_gray 是输入的灰度图像,img_gray.shape[0] 和 img_gray.shape[1] 分别表示图像的高度和宽度。
    3. 计算平均值:代码中使用变量 img_average 存储了一个平均值,用于与图像像素的灰度值进行比较。
    4. 根据亮度值生成哈希值:对于每个像素,代码比较像素的灰度值与平均值 (img_gray[i, j] >= img_average)。如果像素的灰度值大于或等于平均值,就将数字1添加到 img_hash_binary 列表中,表示该像素是亮的。如果像素的灰度值小于平均值,就将数字0添加到 img_hash_binary 列表中,表示该像素是暗的。
    5. 最终哈希值:完成循环后,img_hash_binary 列表将包含图像的二进制哈希值,其中每个元素代表一个像素的明暗情况。

    输出打印:

    对比灰度图像的平均像素值降噪(图像的二进制形式)数组=[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1]
    对比灰度图像的平均像素值降噪(图像的二进制形式)=1111111111101111111101111100001111000011110000111110011111100111       
    
    • 1
    • 2

    或者,使用等价的 lambda 表达式。效果一样。

    # lambda表达式
    img_hash_binary_str = ""
    for i in range(8):
        img_hash_binary_str += ''.join(map(lambda i: '0' if i < img_average else '1', img_gray[i]))
    print(f"对比灰度图像的平均像素值降噪(图像的二进制哈希值)={img_hash_binary_str}")
    
    • 1
    • 2
    • 3
    • 4
    • 5

    输出打印:

    对比灰度图像的平均像素值降噪(图像的二进制形式)=1111111111101111111101111100001111000011110000111110011111100111
    
    • 1

    第五步:生成哈希值

    由于64位二进制值太长,所以按每4个字符为1组,由2进制转成16进制。这样就转为一个长度为16的字符串。这个字符串也就是这个图像可识别的哈希值,也叫图像指纹,即这个图像所包含的特征。

    img_hash = ""
    for i in range(0, 64, 4):
        img_hash += "".join('%x' % int(img_hash_binary_str[i : i + 4], 2))
    print(f"图像可识别的哈希值={img_hash}")
    
    • 1
    • 2
    • 3
    • 4

    代码分解和含义如下:

    1. 初始化为空字符串:创建一个空的字符串 img_hash,用于存储图像哈希值的十六进制表示。
    2. 遍历二进制哈希值:通过循环,代码以4位为一组遍历二进制哈希值 img_hash_binary_str。range(0, 64, 4) 确保代码在哈希值的每4位之间进行迭代。
    3. 将4位二进制转换为一个十六进制字符:在每次循环中,代码取出哈希值中的4位二进制(例如,img_hash_binary_str[i : i + 4]),然后使用’%x’ % int(…, 2) 将这4位二进制转换为一个十六进制字符。int(…, 2) 将二进制字符串转换为整数,‘%x’ 将整数转换为十六进制字符。
    4. 将十六进制字符追加到 img_hash:在每次循环中,得到的十六进制字符将被追加到 img_hash 字符串中。
    5. 最终哈希值:完成循环后,img_hash 将包含图像哈希值的十六进制表示,其中每个字符表示4位二进制。

    输出打印:

    图像可识别的哈希值=ffeff7c3c3c3e7e7
    
    • 1

    第六步:哈希值比较

    通过比较两个图像的哈希值的汉明距离(Hamming Distance),就可以评估图像的相似度,距离越小表示图像越相似。

    def hamming_distance(s1, s2):
        # 检查这两个字符串的长度是否相同。如果长度不同,它会引发 ValueError 异常,因为汉明距离只适用于等长的字符串
        if len(s1) != len(s2):
            raise ValueError("Input strings must have the same length")
        
        distance = 0
        for i in range(len(s1)):
            # 遍历两个字符串的每个字符,比较它们在相同位置上的值。如果发现不同的字符,将 distance 的值增加 1
            if s1[i] != s2[i]:
                distance += 1
        return distance
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    汉明距离: 两个长度相同的字符串在相同位置上的字符不同的个数。即一组二进制数据变成另一组数据所需要的步骤数。汉明距离越小,则相似度越高。汉明距离为0,即两张图片完全一样。

    aHash-0005

     

    4. 测试

    实验场景

    我们来简单测试一下基于均值哈希算法的以图搜图 – 基于一张原图找最相似图片,看看效果如何。

    这里,我准备了10张图片,其中9张是苹果,但形态不一,1张是梨子。

    实验素材

    aHash-0004

    实验代码

    """
    以图搜图:均值哈希算法(Average Hash Algorithm,简称aHash)的原理与实现
    测试环境:win10 | python 3.9.13 | OpenCV 4.4.0 | numpy 1.21.1
    实验时间:2023-10-31
    """
    
    import os
    import cv2
    import time
    import numpy as np
    import matplotlib.pyplot as plt
    
    def get_aHash(img_path):
        # 读取图像:通过OpenCV的imread加载RGB图像
        img_rgb = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2RGB)
        # 缩小图像:使用OpenCV的resize函数将图像缩放为8x8像素,采用Cubic插值方法进行图像重采样
        img_resize = cv2.resize(img_rgb, (8, 8), cv2.INTER_CUBIC)
        # 图像灰度化:将彩色图像转换为灰度图像
        img_gray = cv2.cvtColor(img_resize, cv2.COLOR_BGR2GRAY)
        # print(f"缩放32x32的图像中每个像素的颜色=\n{img_gray}")
    
        # 灰度图像中所有像素的平均值
        img_average = np.mean(img_gray) 
    
        """
        # # 比较平均值:嵌套循环遍历图像的所有像素,对比灰度图像的平均灰度值,转换为二进制的图像哈希值
        # # img_gray:是灰度图像
        # # img_gray.shape[0] 和 img_gray.shape[1] 分别表示图像的高度和宽度
        # img_hash_binary = [] 
        # for i in range(img_gray.shape[0]): 
        #     for j in range(img_gray.shape[1]): 
        #         if img_gray[i,j] >= img_average: 
        #             img_hash_binary.append(1)
        #         else: 
        #             img_hash_binary.append(0)
        # print(f"对比灰度图像的平均像素值降噪(图像的二进制哈希值)数组={img_hash_binary}")
    
        # # 将列表中的元素转换为字符串并连接起来,形成一组64位的图像二进制哈希值字符串
        # img_hash_binary_str = ''.join(map(str, img_hash_binary))
        # print(f"对比灰度图像的平均像素值降噪(图像的二进制哈希值)={img_hash_binary_str}")
    
        # # 生成哈希值
        # img_hash = ""
        # # 遍历二进制哈希值:通过循环,代码以4位为一组遍历二进制哈希值 img_hash_binary_str。
        # # range(0, 64, 4) 确保代码在哈希值的每4位之间进行迭代。
        # for i in range(0, 64, 4):
        #     # 将4位二进制转换为一个十六进制字符
        #     # 在每次循环中,代码取出哈希值中的4位二进制(例如,img_hash_binary_str[i : i + 4])
        #     # 然后使用'%x' % int(..., 2)将这4位二进制转换为一个十六进制字符。
        #     # int(..., 2)将二进制字符串转换为整数,'%x'将整数转换为十六进制字符。
        #     # 将十六进制字符追加到 img_hash:在每次循环中,得到的十六进制字符将被追加到 img_hash 字符串中。
        #     img_hash += "".join('%x' % int(img_hash_binary_str[i : i + 4], 2))
        # print(f"图像可识别的哈希值={img_hash}")
        """
    
        # 遍历图像像素:嵌套循环遍历图像的所有像素,对比灰度图像的平均灰度值,转换为二进制的图像哈希值
        img_hash_binary_str = ''
        for i in range(8):
            img_hash_binary_str += ''.join(map(lambda i: '0' if i < img_average else '1', img_gray[i]))
    
        # 图像可识别哈希值
        img_hash = ''.join(map(lambda x:'%x' % int(img_hash_binary_str[x : x + 4], 2), range(0, 64, 4)))
        return img_hash
    
    
    # 汉明距离:计算两个等长字符串(通常是二进制字符串或位字符串)之间的汉明距离。用于确定两个等长字符串在相同位置上不同字符的数量。
    def hamming_distance(s1, s2):
        # 检查这两个字符串的长度是否相同。如果长度不同,它会引发 ValueError 异常,因为汉明距离只适用于等长的字符串
        if len(s1) != len(s2):
            raise ValueError("Input strings must have the same length")
        
        distance = 0
        for i in range(len(s1)):
            # 遍历两个字符串的每个字符,比较它们在相同位置上的值。如果发现不同的字符,将 distance 的值增加 1
            if s1[i] != s2[i]:
                distance += 1
        return distance
    
    
    # -------------------------------------------------- 测试 --------------------------------------------------
    
    time_start = time.time()
    
    # 指定测试图像库目录
    img_dir = 'img_test'
    # 指定测试图像文件扩展名
    img_suffix = ['.jpg', '.jpeg', '.png', '.bmp', '.gif']
    
    # 获取当前执行脚本所在目录
    script_dir = os.path.dirname(__file__)
    # 获取目标测试图像的全路径
    img_org_path = os.path.join(script_dir, img_dir, 'apple-01.jpg')
    # 获取目标图像可识别哈希值(图像指纹)
    org_img_hash = get_aHash(img_org_path)
    print(f"目标图像:{os.path.relpath(img_org_path)},图片HASH:{org_img_hash}")
    
    # 获取测试图像库中所有文件
    all_files = os.listdir(os.path.join(script_dir, img_dir))
    # 筛选出指定后缀的图像文件
    img_files = [file for file in all_files if any(file.endswith(suffix) for suffix in img_suffix)]
    
    img_hash_all = []
    # 遍历测试图像库中的每张图像
    for img_file in img_files:
        # 获取相似图像文件路径
        img_path = os.path.join(script_dir, img_dir, img_file)
        # 获取相似图像可识别哈希值(图像指纹)
        img_hash = get_aHash(img_path)
        # 获取相似图像与目标图像的汉明距离
        distance = hamming_distance(org_img_hash, img_hash)
        # 存储相似图像的相对路径、哈希值、汉明距离
        img_hash_all.append((os.path.relpath(img_path), img_hash, distance))
    
    for img in img_hash_all:
        print(f"图像:{img[0]},图像HASH:{img[1]},与图像目标的相似值(汉明距离):{img[2]}")
    
    time_end = time.time()
    print(f"耗时:{time_end - time_start}")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118

    输出打印:

    目标图像:..\..\P1_Hash\01_aHash\img_test\apple-01.jpg,图片HASH:ffeff7c3c3c3e7e7
    图像:..\..\P1_Hash\01_aHash\img_test\apple-01.jpg,图像HASH:ffeff7c3c3c3e7e7,与图像目标的相似值(汉明距离):0
    图像:..\..\P1_Hash\01_aHash\img_test\apple-02.jpg,图像HASH:ffcfc3e3e3e3e7ff,与图像目标的相似值(汉明距离):8
    图像:..\..\P1_Hash\01_aHash\img_test\apple-03.jpg,图像HASH:ffe7c3c3c3c7c7ff,与图像目标的相似值(汉明距离):7
    图像:..\..\P1_Hash\01_aHash\img_test\apple-04.jpg,图像HASH:e7e7c3c3c3eff7ff,与图像目标的相似值(汉明距离):10
    图像:..\..\P1_Hash\01_aHash\img_test\apple-05.jpg,图像HASH:f3f3e7c7c3c7c7e7,与图像目标的相似值(汉明距离):7
    图像:..\..\P1_Hash\01_aHash\img_test\apple-06.jpg,图像HASH:ffffd981818189dd,与图像目标的相似值(汉明距离):13
    图像:..\..\P1_Hash\01_aHash\img_test\apple-07.jpg,图像HASH:fff7f3e3e3e3f0ff,与图像目标的相似值(汉明距离):10
    图像:..\..\P1_Hash\01_aHash\img_test\apple-08.jpg,图像HASH:000006fdf171f9f8,与图像目标的相似值(汉明距离):16
    图像:..\..\P1_Hash\01_aHash\img_test\apple-09.jpg,图像HASH:ffcfe7c1c1c3e7ff,与图像目标的相似值(汉明距离):6
    图像:..\..\P1_Hash\01_aHash\img_test\pear-001.jpg,图像HASH:fffbe5c1c3c3c3ef,与图像目标的相似值(汉明距离):8
    耗时:0.09973335266113281
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    简单的测试分析:

    原图相似图片相似值(汉明距离)相似图片特点相似图片与原图Hash对比结果
    图片01图片010自己自己与自己相似度100%
    图片01图片096青苹果最相似。相同背景相同物体位置下最相似。
    图片01图片03、图片057红蛇果(苹果)、青苹果(2D)次相似。同上,单物体对比时,背景、物体位置越近越相似。
    图片01图片028两者几乎相似比较相似。影响相似距离的似乎是苹果下方的阴影有无。
    图片01图片pear-0018黄色的梨子意外相似。相似搜索并不能识别物体/内容,因为工作原理是通过图片灰度后的灰色像素点位置与对比。
    图片01图片0410原图像的180度旋转图相差甚远。对于原图旋转变换相对不敏感,因为均值哈希算法只捕获了图像的平均亮度和粗略结构。
    图片01图片06、07、0810以上复杂、多主体、多色调较难分辨。复杂、多主体、多色调的图片较难与原图相似。

    10张测试图片中,汉明距离在5以内1张;汉明距离在5以外9张。
    从抽样简单测试结果看,平均哈希简单且计算速度快,但它对图像的细节变化比较敏感,容易受到局部图像的特性的干扰。

    备注:如果汉明距离0,则表示这两张图片非常相似;如果汉明距离小于5,则表示有些不同,但比较相近;如果汉明距离大于10,则表明是完全不同的图片。

     

    5. 总结

    经过实验和测试,平均哈希算法优缺点明显。

    特点: 传统,属于一种外观相似哈希算法。
    优点: 简单、计算效率高,适用于快速图像相似性比较。
    缺点: 对于图片的旋转和主体内容变换相对不敏感;对于复杂、多主体、多色调的图片较难相似,因为它只捕获了图片的平均亮度和粗略结构。

     

    6. 实验问题

    问题1: 为什么通过 cv2.imread(img_path) 加载的图像,显示出来之后,原图由红色变成了蓝色?
    aHash-0006
    问题原因: 如果原图是红色的,但通过OpenCV加载显示的图像是蓝色的,这可能是由于图像的通道顺序不同导致的。在OpenCV中,图像的通道顺序通常是BGR(蓝绿红),而在一些其他库(如matplotlib)中,通常使用RGB(红绿蓝)通道顺序。
    解决方案: 使用OpenCV的通道重排功能,将图像的通道顺序从BGR转换为RGB,然后再显示图像。以下是修改后的代码:

    # 通过OpenCV加载图像
    img = cv2.imread(img_path)
    
    # 通道重排,从BGR转换为RGB
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    
    • 1
    • 2
    • 3
    • 4
    • 5

     

    问题2: 为什么使用了 cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ,但显示出来图像是彩色的?
    aHash-0007
    问题原因: 这可能是由于你使用了 matplotlib 来显示图像,而 matplotlib 默认将灰度图像显示为伪彩色图像。Matplotlib会将单通道的灰度图像(每个像素只有一个亮度值)显示为伪彩色图像以便于可视化。
    解决方案: 在使用 imshow 函数显示图像时,添加 cmap 参数,并将其设置为 ‘gray’,以确保图像以灰度形式显示。例如:

    # 测试图片路径
    img_path = 'img_test/apple-01.jpg'
    
    # 通过OpenCV加载图像
    img = cv2.imread(img_path)
    
    # 通道重排,从BGR转换为RGB
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    
    # 使用OpenCV的resize函数将图像缩放为8x8像素,采用Cubic插值方法
    img_resize = cv2.resize(img_rgb, (8, 8), cv2.INTER_CUBIC)
    
    # 灰度化:将彩色图像转换为灰度图像。
    img_gray = cv2.cvtColor(img_resize, cv2.COLOR_BGR2GRAY)
    
    # 灰度形式查看图像
    plt.imshow(img_gray, cmap='gray')
    # 显示图像
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

     

    7. 系列书签

    OpenCV书签 #均值哈希算法的原理与相似图片搜索实验
    OpenCV书签 #感知哈希算法的原理与相似图片搜索实验
    OpenCV书签 #差值哈希算法的原理与相似图片搜索实验
    OpenCV书签 #直方图算法的原理与相似图片搜索实验

  • 相关阅读:
    JavaScript高级复习下(60th)
    (四)JPA - JQPL 实现增删改查
    Spring事务简介说明
    C++Primer第七章:类
    Flink系列文档-(YY05)-Flink编程API-多流算子
    AI带你省钱旅游!精准预测民宿房源价格! ⛵
    0xC004F069错误的解决方案
    wallet connect简单使用
    女同桌找我要表情包,还好我会Python,分分钟给她下载几十个G...
    基础会计学模拟卷
  • 原文地址:https://blog.csdn.net/itanping/article/details/134022120