OpenCV #以图搜图:均值哈希算法(Average Hash Algorithm)原理与实验
1. 介绍
均值哈希算法(Average Hash Algorithm) 是哈希算法的一种,主要用来做相似图片的搜索工作。
2. 原理
均值哈希算法(aHash)首先将原图像缩小成一个固定大小的像素图像,然后将图像转换为灰度图像,通过缩小图像的每个像素与平均灰度值的比较,生成一组哈希值。最后,利用两组图像的哈希值的汉明距离来评估图像的相似度。
魔法: 概括地讲,均值哈希算法一共可细分六步:
- 缩小图像: 将目标图像缩小为一个固定的大小,通常为8x8像素,总共64个像素。作用是去除各种图像尺寸和图像比例的差异,只保留结构、明暗等基本信息,目的是确保图像的一致性,降低计算的复杂度。
- 图像灰度化: 将缩小的图像转换为灰度图像。
- 灰度平均值: 计算灰度图像的平均灰度值。减少计算量。
- 比较平均值: 遍历灰度图像的每个像素,比较每个像素的灰度值是否大于或小于平均值。对于大于等于平均值的像素,将其表示为1,对于小于平均值的像素,将其表示为0。最后,得到一个64位的二进制值(8x8像素的图像)。
- 生成哈希值: 由于64位二进制值太长,所以按每4个字符为1组,由2进制转成16进制。这样就转为一个长度为16的字符串。这个字符串也就是这个图像可识别的哈希值,也叫图像指纹,即这个图像所包含的特征。
- 哈希值比较: 通过比较两个图像的哈希值的汉明距离(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)
2)缩小原图
# 缩小图像:使用OpenCV的resize函数将图像缩放为8x8像素,采用Cubic插值方法进行图像重采样
img_resize = cv2.resize(img, (8, 8), cv2.INTER_CUBIC)
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]]
第三步:灰度平均值
计算灰度图像的平均灰度值。减少计算量。
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}")
代码分解和含义如下:
- 初始化空列表:创建一个空的列表 img_hash_binary,用于存储图像的哈希值。
- 遍历图像像素:嵌套循环遍历图像的所有像素,其中 img_gray 是输入的灰度图像,img_gray.shape[0] 和 img_gray.shape[1] 分别表示图像的高度和宽度。
- 计算平均值:代码中使用变量 img_average 存储了一个平均值,用于与图像像素的灰度值进行比较。
- 根据亮度值生成哈希值:对于每个像素,代码比较像素的灰度值与平均值 (img_gray[i, j] >= img_average)。如果像素的灰度值大于或等于平均值,就将数字1添加到 img_hash_binary 列表中,表示该像素是亮的。如果像素的灰度值小于平均值,就将数字0添加到 img_hash_binary 列表中,表示该像素是暗的。
- 最终哈希值:完成循环后,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}")
代码分解和含义如下:
- 初始化为空字符串:创建一个空的字符串 img_hash,用于存储图像哈希值的十六进制表示。
- 遍历二进制哈希值:通过循环,代码以4位为一组遍历二进制哈希值 img_hash_binary_str。range(0, 64, 4) 确保代码在哈希值的每4位之间进行迭代。
- 将4位二进制转换为一个十六进制字符:在每次循环中,代码取出哈希值中的4位二进制(例如,img_hash_binary_str[i : i + 4]),然后使用’%x’ % int(…, 2) 将这4位二进制转换为一个十六进制字符。int(…, 2) 将二进制字符串转换为整数,‘%x’ 将整数转换为十六进制字符。
- 将十六进制字符追加到 img_hash:在每次循环中,得到的十六进制字符将被追加到 img_hash 字符串中。
- 最终哈希值:完成循环后,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 = 0for i in range(len(s1)):# 遍历两个字符串的每个字符,比较它们在相同位置上的值。如果发现不同的字符,将 distance 的值增加 1if s1[i] != s2[i]:distance += 1return distance
4. 测试
我们来简单测试一下基于均值哈希算法的以图搜图 – 基于一张原图找最相似图片,看看效果如何。
这里,我准备了10张图片,其中9张是苹果,但形态不一,1张是梨子。
输出打印:
图片名称: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
汉明距离:两个长度相同的字符串在相同位置上的字符不同的个数。
简单的测试分析:
原图 | 相似图片 | 相似值(汉明距离) | 相似图片特点 | 相似图片与原图Hash对比结果 |
---|---|---|---|---|
图片01 | 图片01 | 0 | 自己 | 自己与自己相似度100% |
图片01 | 图片09 | 6 | 青苹果 | 最相似。相同背景相同物体位置下最相似。 |
图片01 | 图片03、图片05 | 7 | 红蛇果(苹果)、青苹果(2D) | 次相似。同上,单物体对比时,背景、物体位置越近越相似。 |
图片01 | 图片02 | 8 | 两者几乎相似 | 比较相似。影响相似距离的似乎是苹果下方的阴影有无。 |
图片01 | 图片pear-001 | 8 | 黄色的梨子 | 意外相似。相似搜索并不能识别物体/内容,因为工作原理是通过图片灰度后的灰色像素点位置与对比。 |
图片01 | 图片04 | 10 | 原图像的180度旋转图 | 相差甚远。对于原图旋转变换相对不敏感,因为均值哈希算法只捕获了图像的平均亮度和粗略结构。 |
图片01 | 图片06、07、08 | 10以上 | 复杂、多主体、多色调 | 较难分辨。复杂、多主体、多色调的图片较难与原图相似。 |
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 pltdef 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 = 0for i in range(len(s1)):# 遍历两个字符串的每个字符,比较它们在相同位置上的值。如果发现不同的字符,将 distance 的值增加 1if s1[i] != s2[i]:distance += 1return 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) 加载的图像,显示出来之后,原图由红色变成了蓝色?
问题原因: 如果原图是红色的,但通过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) ,但显示出来图像是彩色的?
问题原因: 这可能是由于你使用了 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()
相关文章:

OpenCV #以图搜图:均值哈希算法(Average Hash Algorithm)原理与实验
1. 介绍 均值哈希算法(Average Hash Algorithm) 是哈希算法的一种,主要用来做相似图片的搜索工作。 2. 原理 均值哈希算法(aHash)首先将原图像缩小成一个固定大小的像素图像,然后将图像转换为灰度图像&am…...

博通BCM575系列RDMA网卡驱动bnxt_re分析(一)
简介 整个BCM系列驱动分成以太网部分(bnxt_en.ko)和RDMA部分(bnxt_re.ko), 两个模块之间通过内核的auxiliary_bus进行管理.我们主要分析下bnxt_re驱动. 代码结构 这个驱动的核心是 qplib_fp.c, 这个文件主要包含了驱动的数据路径, 包括Post Send, Post Recv, Poll CQ流程的实…...

集合总结-
Collection 常用方法 package com.test01;import java.util.ArrayList; import java.util.Collection; /*添加元素---boolean add(E e);移除元素---boolean remove(Object c);判断元素是否存在---boolean contains(Object c);*/ public class S {public static void main(Str…...

【知识串联】概率论中的值和量(随机变量/数字特征/参数估计)【考研向】【按概率论学习章节总结】
就我的概率论学习经验来看,这两个概念极易混淆,并且极为重点,然而,在概率论的前几章学习中,如果只是计算,对这方面的辨析不清并没有问题。然而,到了后面的参数估计部分,却可能出现问…...

上游服务不可用了,下游服务如何应对?
上游服务不可用了,下游服务如何应对? 引言 在系统中,上游服务和下游服务是两个关键概念。上游服务通常指的是提供某种功能或数据的服务端,它接收来自下游服务的请求,并根据请求进行处理和响应。下游服务通常指的是发…...

WebGL笔记:矩阵的变换之平移的实现
矩阵的变换 变换 变换有三种状态:平移、旋转、缩放。当我们变换一个图形时,实际上就是在移动这个图形的所有顶点。解释 webgl 要绘图的话,它是先定顶点的,就比如说我要画个三角形,那它会先把这三角形的三个顶点定出来…...

XTU-OJ 1187-Candy
WCB某天买了非常多的糖果并把它们分成N份,依次分别有1,2,3…,N个糖果。他想拿出其中的3份分给他的室友, 为了不让室友们闹意见,必须让这三份的糖果总数恰好能被三人均分。请问他一共有多少种不同的组合方案数ÿ…...

基于 nodejs+vue城市轨道交通线路查询系统mysql
目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…...

电商时代,VR全景如何解决实体店难做没流量?
近日,电商和实体经济的对立成为了热门话题,尽管电商的兴起确实对线下实体店造成了一定的冲击,但实体店也不是没有办法挽救。VR全景助力线下实体店打造线上店铺,打通流量全域布局,还能实现打开产品、查看产品内部细节等…...

操作系统-浅谈CPU与内存
目录 计算机的基本组成CPU内存虚拟内存内存分段内存分页 CPU与内存的交互过程高速缓存cache 所有图片均来自:小林coding 计算机的基本组成 计算机由软件和硬件组成 硬件由CPU(中央处理器)存储器(内存外存)外部设备组成。 软件由应用软件和系…...

K8s 部署 CNI 网络组件+k8s 多master集群部署+负载均衡
------------------------------ 部署 CNI 网络组件 ------------------------------ ---------- 部署 flannel ---------- K8S 中 Pod 网络通信: ●Pod 内容器与容器之间的通信 在同一个 Pod 内的容器(Pod 内的容器是不会跨宿主机的)共享同一…...

若依微服务上传图片文件代理配置
在使用若依微服务文件上传时候,文件上传成功会上传到D:/ruoyi/uploadPath目录下。默认使用9300端口进行访问图片文件,现在我想把它代理到80端口应该怎么做呢? 配置前:http://localhost:9300/statics/2023/09/24/test.jpg 配置后:http://localhost/statics/2023/09/24/test…...

物联网与 Linux 的相爱相生
Linux 无疑将在物联网中扮演一个关键角色,但是其光彩将与其它的一些分享。 随着 Canonical 重新关注于赢利和新技术,我们中的一些人发现我们正在思考 Linux 未来将走向何方,IoT(物联网)是否是 Linux 的未来࿱…...

python自动化测试(一):操作浏览器
通过Python的代码去操作浏览器的操作 目录 目录 1、导入自动化模块 2、定义打开的浏览器驱动、声明一个url变量保存打开的地址 3、使用函数:driver.get(url)打开浏览器的指定页面 4、最大化浏览器窗口:driver.maximize_window() 5、添加全局的等待…...

NReco.LambdaParser使用案例
使用案例集合: private async void RuleEngine_Click(object sender, EventArgs e){#region 获取变量string expression this.Rule.Text.Trim();string pattern "\$(.*?)\$";MatchCollection matches Regex.Matches(expression, pattern);foreach (Ma…...

苹果IOS安装IPA, plist形式 Safari 浏览器点击安装
快速链接 苹果开发者账号链接 网址: https://developer.apple.com/account 苹果应用上架链接 网址: https://appstoreconnect.apple.com/ 应用证书文件及打包 参考教程: 最新uniapp打包IOS详细步骤(2022) 证书在线制作工具 网址: https://app.121xuexi.…...

Django 注册及创建订单商品
注册功能的实现 user/views from rest_framework.generics import GenericAPIView from rest_framework.views import APIViewfrom apps.user.models import User from apps.user.serializers import UserSerializer from utils import ResponseMessage from utils.jwt_auth …...

15、Python -- 阶段总结:变量与流程控制
目录 变量变量没有类型,数据有类型 表达式程序流程 变量 变量:编程的本质就是处理数据,数据需要用变量保存 Python语言的特征: 所有变量无需声明,即可使用 变量没有类型 变量没有类型,数据有类型 已学过…...

信息检索与数据挖掘 | 【实验】排名检索模型
文章目录 📚实验内容📚相关概念📚实验步骤🐇分词预处理🐇构建倒排索引表🐇计算query和各个文档的相似度🐇queries预处理及检索函数🔥对输入的文本进行词法分析和标准化处理…...

玩转AIGC:打造令人印象深刻的AI对话Prompt
玩转AIGC:打造令人印象深刻的AI对话Prompt 《玩转AIGC:打造令人印象深刻的AI对话Prompt》摘要引言正文良好的Prompt:引发AI深度交流的法宝 ✨探讨不同的提问方式1. 常规提问2. 创意提问 对话交流的艺术:倾听与引导的巧妙平衡 ⚖️…...

uniapp vue国际化 i18n
一、安装 vue-i18n npm i vue-i18n 二、新建i18n目录 1、en.json 内容 {"loginPage":{"namePh":"Please enter your login account","passwordPh":"Please enter password"} } 2、zh-CN.json 内容 {"loginPage&qu…...

Docker 启动远程服务访问不了
今天一下午在弄这个 1、防火墙是否关了 firewall-cmd --state2、ip转发开没开 sysctl net.ipv4.ip_forward3、service iptables是不是打开并拦截了 4、检查docker启动的端口号是否一致,或者启动时对不对 5、检查docker的服务是否起来了,比如你的端口号…...

[ACTF2020 新生赛]Exec
【解题过程】 1.打开链接 得到一个能ping 的网站,可以推测这个可以在终端运行的网站。 2.解题思路 在执行的时候我们可以想到命令执行的“;”分号的作用:命令用分号分隔开来,表示它们是两个独立的命令,需要依次执行。…...

Git(三).git 文件夹详解
目录 一、初始化新仓库二、.git 目录2.1 hooks 文件夹2.2 info 文件夹2.3 logs 文件夹2.4 objects 文件夹【重要】2.5 refs 文件夹【重要】2.6 COMMIT_EDITMSG2.7 config2.8 description2.9 FETCH_HEAD2.10 HEAD【重要】2.11 index【重要】2.12 ORIG_HEAD2.13 packed-refs 官网…...

esp32-S3 + visual studio code 开发环境搭建
一、首先在下面链接网页中下载esp-idf v5.1.1离线安装包 ,并安装到指定位置。dl.espressif.cn/dl/esp-idf/https://dl.espressif.cn/dl/esp-idf/ 安装过程中会提示需要长路径支持,所以windows系统需要开启长路径使能 Step 1: 打开运行&…...

4.1 网络基础之网络IO
一、编写基本服务程序流程 1、创建套接字 #include <sys/types.h> #include <sys/socket.h>int socket(int domain, int type, int protocol);/* * 参数domain通讯协议族: * PF_INET IPv4互联网协议族(常用) * PF_INET6 …...

[100天算法】-和为 K 的子数组(day 39)
题目描述 给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。示例 1 :输入:nums [1,1,1], k 2 输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。 说明 :数组的长度为 [1, 20,000]。 数组中元素的范围是 [-1000, 1000] ,且整…...

Leo赠书活动-02期 【信息科技风险管理:合规管理、技术防控与数字化】
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 赠书活动专栏 ✨特色专栏:…...

L2-026 小字辈 - java
L2-026 小字辈 时间限制 400 ms 内存限制 64 MB 题目描述: 本题给定一个庞大家族的家谱,要请你给出最小一辈的名单。 输入格式: 输入在第一行给出家族人口总数 N(不超过 100 000 的正整数) —— 简单起见,…...

排序算法-堆积树排序法(HeapSort)
目录 排序算法-堆积树排序法(HeapSort) 1、说明 2、算法分析 3、C代码 排序算法-堆积树排序法(HeapSort) 1、说明 堆积树排序法是选择排序法的改进版,可以减少在选择排序法中的比较次数,进而减少排序…...