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

1. 介绍

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

 

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)

aHash-0001

2)缩小原图

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

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}")

输出打印:

缩放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]]

aHash-0003

第三步:灰度平均值

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

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

输出打印:

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

第四步:比较平均值

遍历灰度图像的每个像素,比较每个像素的灰度值是否大于或小于平均值。对于大于等于平均值的像素,将其表示为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. 初始化空列表:创建一个空的列表 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       

或者,使用等价的 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}")

输出打印:

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

第五步:生成哈希值

由于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. 初始化为空字符串:创建一个空的字符串 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

第六步:哈希值比较

通过比较两个图像的哈希值的汉明距离(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

 

4. 测试

我们来简单测试一下基于均值哈希算法的以图搜图 – 基于一张原图找最相似图片,看看效果如何。
这里,我准备了10张图片,其中9张是苹果,但形态不一,1张是梨子。
aHash-0004
输出打印:

图片名称:img_test/apple-01.jpg,图片HASH:ffeff7c3c3c3e7e7,与图片1的近似值(汉明距离):0
图片名称:img_test/apple-02.jpg,图片HASH:ffcfc3e3e3e3e7ff,与图片1的近似值(汉明距离):8
图片名称:img_test/apple-03.jpg,图片HASH:ffe7c3c3c3c7c7ff,与图片1的近似值(汉明距离):7
图片名称:img_test/apple-04.jpg,图片HASH:e7e7c3c3c3eff7ff,与图片1的近似值(汉明距离):10
图片名称:img_test/apple-05.jpg,图片HASH:f3f3e7c7c3c7c7e7,与图片1的近似值(汉明距离):7
图片名称:img_test/apple-06.jpg,图片HASH:ffffd981818189dd,与图片1的近似值(汉明距离):13
图片名称:img_test/apple-07.jpg,图片HASH:fff7f3e3e3e3f0ff,与图片1的近似值(汉明距离):10
图片名称:img_test/apple-08.jpg,图片HASH:000006fdf171f9f8,与图片1的近似值(汉明距离):16
图片名称:img_test/apple-09.jpg,图片HASH:ffcfe7c1c1c3e7ff,与图片1的近似值(汉明距离):6
图片名称:img_test/pear-001.jpg,图片HASH:fffbe5c1c3c3c3ef,与图片1的近似值(汉明距离):8
耗时:0.09571695327758789

汉明距离:两个长度相同的字符串在相同位置上的字符不同的个数。
aHash-0005

简单的测试分析:

原图相似图片相似值(汉明距离)相似图片特点相似图片与原图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. 实验代码

"""
以图搜图:均值哈希算法(Average Hash Algorithm)的原理与实现
测试环境:win10 | python 3.9.13 | OpenCV 4.4.0 | numpy 1.21.1
实验时间:2023-10-20
"""

import cv2
import time
import numpy as np
import matplotlib.pyplot as plt

def get_hash(img_path):
    # 读取图像:通过OpenCV的imread加载图像
    # 缩小图像:使用OpenCV的resize函数将图像缩放为8x8像素,采用Cubic插值方法进行图像重采样
    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"缩放8x8的图像中每个像素的颜色=\n{img_gray}")

    # 灰度平均值:计算灰度图像的平均灰度值
    img_average = np.mean(img_gray) 
    # print(f"灰度图像中所有像素的平均值={img_average}")

    """
    # # 比较平均值:嵌套循环遍历图像的所有像素,对比灰度图像的平均灰度值,转换为二进制的图像哈希值
    # # 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]))
    # print(f"对比灰度图像的平均像素值降噪(图像的二进制哈希值)={img_hash_binary_str}")

    # 图像可识别哈希值
    img_hash = ''.join(map(lambda x:'%x' % int(img_hash_binary_str[x : x + 4], 2), range(0, 64, 4)))
    # print(f"图像可识别的哈希值={img_hash}")
    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_1 = 'img_test/apple-01.jpg'
img_2 = 'img_test/apple-02.jpg'
img_3 = 'img_test/apple-03.jpg'
img_4 = 'img_test/apple-04.jpg'
img_5 = 'img_test/apple-05.jpg'
img_6 = 'img_test/apple-06.jpg'
img_7 = 'img_test/apple-07.jpg'
img_8 = 'img_test/apple-08.jpg'
img_9 = 'img_test/apple-09.jpg'
img_10 = 'img_test/pear-001.jpg'

img_hash1 = get_hash(img_1)
img_hash2 = get_hash(img_2)
img_hash3 = get_hash(img_3)
img_hash4 = get_hash(img_4)
img_hash5 = get_hash(img_5)
img_hash6 = get_hash(img_6)
img_hash7 = get_hash(img_7)
img_hash8 = get_hash(img_8)
img_hash9 = get_hash(img_9)
img_hash10 = get_hash(img_10)

distance1 = hamming_distance(img_hash1, img_hash1)
distance2 = hamming_distance(img_hash1, img_hash2)
distance3 = hamming_distance(img_hash1, img_hash3)
distance4 = hamming_distance(img_hash1, img_hash4)
distance5 = hamming_distance(img_hash1, img_hash5)
distance6 = hamming_distance(img_hash1, img_hash6)
distance7 = hamming_distance(img_hash1, img_hash7)
distance8 = hamming_distance(img_hash1, img_hash8)
distance9 = hamming_distance(img_hash1, img_hash9)
distance10 = hamming_distance(img_hash1, img_hash10)

time_end = time.time()

print(f"图片名称:{img_1},图片HASH:{img_hash1},与图片1的近似值(汉明距离):{distance1}")
print(f"图片名称:{img_2},图片HASH:{img_hash2},与图片1的近似值(汉明距离):{distance2}")
print(f"图片名称:{img_3},图片HASH:{img_hash3},与图片1的近似值(汉明距离):{distance3}")
print(f"图片名称:{img_4},图片HASH:{img_hash4},与图片1的近似值(汉明距离):{distance4}")
print(f"图片名称:{img_5},图片HASH:{img_hash5},与图片1的近似值(汉明距离):{distance5}")
print(f"图片名称:{img_6},图片HASH:{img_hash6},与图片1的近似值(汉明距离):{distance6}")
print(f"图片名称:{img_7},图片HASH:{img_hash7},与图片1的近似值(汉明距离):{distance7}")
print(f"图片名称:{img_8},图片HASH:{img_hash8},与图片1的近似值(汉明距离):{distance8}")
print(f"图片名称:{img_9},图片HASH:{img_hash9},与图片1的近似值(汉明距离):{distance9}")
print(f"图片名称:{img_10},图片HASH:{img_hash10},与图片1的近似值(汉明距离):{distance10}")

print(f"耗时:{time_end - time_start}")

 

7. 疑难杂症

问题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)

问题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()

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/105725.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

何为自制力?如何提高自制力?

什么是自制力&#xff1f; 自制力也即是自我控制能力&#xff0c;是一个人如何去抵御外部诱惑力&#xff0c;从而坚持自己的原本计划&#xff0c;坚定去完成目标。除了外部诱惑力&#xff0c;也可以指的是面对困境&#xff0c;不良情绪等外部因素。 自制力是自我管理能力的体…

Python-股票市场用于算法交易的人类反馈强化学习 (RLHF)

ChatGPT 的成功使人类反馈强化学习 (RLHF) 技术成为人们关注的焦点。RLHF 是一种机器学习方法,它结合了强化学习 (RL) 和人类反馈 (HF) 来改进学习过程。这篇文章将使您对 RLHF 有一个全面的了解。它描述了 RLHF 在算法交易(algo transactions)中的应用,并提供了可执行的 P…

机器学习第一周

一、概述 机器学习大致会被划分为两类&#xff1a;监督学习&#xff0c;无监督学习 1.1 监督学习 监督学习其实就是&#xff0c;给计算机一些输入x和正确的输出y&#xff08;训练数据集&#xff09;&#xff0c;让他总结x->y的映射关系&#xff0c;从而给他其他的输入x&a…

设计模式(五)—— 建造者模式/生成器模式

先简单记一下&#xff0c;以后再来认真写 还是造房子那个例子&#xff0c;一个房子分为①打地基 ② 砌墙 ③封顶三步&#xff0c;如果不用设计模式去写的话。就是一个超类&#xff0c;然后多个子类继承超类去重写 但是这样有两个缺点&#xff1a; &#xff08;1&#xff09;产…

高效技巧揭秘:Java轻松批量插入或删除Excel行列操作

摘要&#xff1a;本文由葡萄城技术团队原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 在职场生活中&#xff0c;对Excel工作表的行和列进行操作是非常普遍的需求。一般情况下…

电脑扬声器未插入?4个方法帮你恢复声音!

“太奇怪了吧&#xff0c;我的电脑扬声器一直显示未插入&#xff0c;我使用电脑的时候也是一直都没有声音。这是为什么呢&#xff1f;我应该怎么解决这个问题呀&#xff1f;” 我们使用电脑播放音频或视频时&#xff0c;都需要用到电脑扬声器。如果扬声器无法播放声音&#xff…

NLP入门——语言结构/语言建模

一、Linguistics 语言学 wordsmorphology 形态学&#xff1a;词的构成和内部结构研究。如英语的dog、dogs和dog-catcher有相当的关系morpheme 语素&#xff1a;最小的语法单位&#xff0c;是最小的音义结合体lexeme 词位&#xff1a;词的意义的基本抽象单位&#xff0c;是一组…

基于机器视觉的手势检测和识别算法 计算机竞赛

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的手势检测与识别算法 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f9ff; 更多资料, 项目分享&#xff1a; https://gitee.com/dancheng…

低代码软件的价格考量:成本效益与投资回报

数字化转型的今天&#xff0c;我们常听到“低代码”这个概念&#xff0c;那低代码软件价格到底如何呢&#xff1f;很多厂商并没有公布软件价格情况&#xff0c;让很多企业在采购的时候也是一头雾水。当然&#xff0c;市场上也存在一些厂商公开透明价格&#xff0c;比如Zoho Cre…

STM32 HAL库串口使用printf

STM32 HAL库串口使用printf 背景配置说明在usart.h中添加在usart.c中添加在工程中选中微库&#xff1a; 测试 背景 在我们使用CubeMX生成好STM32 HAL库工程之后&#xff0c;我们想使用printf函数来打印一些信息&#xff0c;配置如下&#xff1a; 配置说明 在usart.h中添加 …

使用了lua-resty-http库进行 爬虫

lua-resty-http是一个基于OpenResty的HTTP客户端库&#xff0c;用于在Lua中进行HTTP请求和响应的处理。它提供了简单易用的接口&#xff0c;可以方便地进行网页抓取和爬虫开发。 使用lua-resty-http进行爬虫&#xff0c;需要先安装OpenResty和lua-resty-http库&#xff0c;并将…

电子学会C/C++编程等级考试2023年05月(三级)真题解析

C/C等级考试&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;找和为K的两个元素 在一个长度为n(n < 1000)的整数序列中&#xff0c;判断是否存在某两个元素之和为k。 输入 第一行输入序列的长度n和k&#xff0c;用空格分开。 第二行输入序列中的n个整数&am…

sqoop和flume简单安装配置使用

1. Sqoop 1.1 Sqoop介绍 Sqoop 是一个在结构化数据和 Hadoop 之间进行批量数据迁移的工具 结构化数据可以是MySQL、Oracle等关系型数据库 把关系型数据库的数据导入到 Hadoop 与其相关的系统 把数据从 Hadoop 系统里抽取并导出到关系型数据库里 底层用 MapReduce 实现数据 …

NCP1256ESN65T1G具有多种保护功能 一款低功率离线电流模式PWM控制器

NCP1256ESN65T1G 包括构建几瓦到几十瓦成本高效开关模式电源所需的一切功能。该零件采用微型 TSOP-6 封装&#xff0c;供电范围高达 30 V&#xff0c;具有带抖动的 65 kHz 或 100 kHz 开关电路&#xff0c;在峰值电流模式控制下运行。当辅助侧功率开始降低时&#xff0c;该控制…

Autojs 利用OpenCV识别棋子之天天象棋你马没了

本例子通过代码像你介绍利用OpenCV实现霍尔找圆的方法定位棋子位置 通过autojs脚本实现自动点击棋子 开源地址 https://github.com/Liberations/TtxqYourHorseIsGone/blob/master/main.js AutoXJs https://github.com/kkevsekk1/AutoX/releasesauto() //安卓版本高于Android 9…

Elasticsearch(十五)搜索---搜索匹配功能⑥--基于地理位置查询

一、前言 随着互联网的热门&#xff0c;越来越多的传统行业将全部或者部分业务转移到互联网上&#xff0c;其中不乏一些和地理位置强相关的行业。基于地理位置的搜索功能&#xff0c;大大提升了人们的生活和工作效率。例如&#xff0c;外出旅行时&#xff0c;只需要用手机打开…

NineData:高效、安全、可靠的DB2数据管理平台

Db2 是老牌厂商 IBM 研发和维护的关系型数据库管理系统。作为一个拥有悠久历史的数据库系统&#xff0c;Db2 凭借它的高可靠、可扩展和高安全性等诸多优点&#xff0c;在如今的数据库市场依然占据相当大的份额。 对于诸多金融行业的企业而言&#xff0c; Db2 作为承载其核心业务…

R语言的物种气候生态位动态量化与分布特征模拟实践技术

在全球气候快速变化的背景下&#xff0c;理解并预测生物种群如何应对气候变化&#xff0c;特别是它们的地理分布如何变化&#xff0c;已经变得至关重要。利用R语言进行物种气候生态位动态量化与分布特征模拟&#xff0c;不仅可以量化描述物种对环境的需求和适应性&#xff0c;预…

【开源】基于SpringBoot的农村物流配送系统的设计和实现

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 系统登录、注册界面2.2 系统功能2.2.1 快递信息管理&#xff1a;2.2.2 位置信息管理&#xff1a;2.2.3 配送人员分配&#xff1a;2.2.4 路线规划&#xff1a;2.2.5 个人中心&#xff1a;2.2.6 退换快递处理&#xff1a;…

Windows server部署filebeat到kafka

需求&#xff1a;Windows dhcp日志需要实时传输到elk或者其他告警平台。 1、filebeat下载地址&#xff1a;https://www.elastic.co/cn/downloads/beats/filebeat 2、下载后解压后配置filebeat.yml文件&#xff0c; 3、README.md文件中有运行的操作方法&#xff1a;cmd上进入f…