使用OpenCV实现以图搜图
什么是以图搜图?
以图搜图,简单来说,就是通过搜索图像的文本或视觉特征,帮助用户找到与这张图片相似或相关的其他图形图像资料。
感知哈希算法
感知哈希算法(Perceptual Hashing Algorithm,简称PHA或PHash)是一种用于检测非结构化数据(如图像、视频、音频)相似性的技术。其原理是利用数据的频谱特征来描述对象,并通过一定的数学方法计算出一个哈希码(Hash String),以此来表示该对象的特征。当数据的频谱特征发生变化时,其对应的哈希码也会相应改变。
算法实现步骤
感知哈希算法实现步骤主要包括以下几步:
- 缩小尺寸:将图像缩小到一定的尺寸,比如8x8,总共64个像素。这一步的目的是去除图像的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图像差异。
- 简化色彩:将缩小后的图像转为灰度图,即所有像素点都转换为灰度值。简化色彩有助于突出图像的主要特征,降低计算的复杂度。
- 计算平均值:计算所有像素的灰度平均值。这个平均值将作为后续比较像素灰度的基准。
- 比较像素灰度:将每个像素的灰度值与平均值进行比较。如果像素的灰度值大于或等于平均值,则记录为1,否则记录为0。
- 计算哈希值:将上一步的比较结果组合在一起,形成一个固定长度的哈希值。这个哈希值就是图像的“指纹”,代表了图像的主要特征。
- 对比图片指纹:在生成了每个图像的哈希值(即指纹)之后,就可以开始对比不同的图片指纹了。通常,会计算两个指纹之间的差异度,例如,通过比较两个哈希值中不同位的数量。如果差异度低于某个预设的阈值,就认为这两张图片是相似的;如果差异度高于阈值,则认为它们是不同的图片。
效果
要搜索的图片:
搜索的图片库:
匹配出3个最接近的图片:
源码
import glob
import cv2
import numpy as np
import matplotlib.pyplot as plt
# ------------------ 辅助函数 ------------------
def resize_and_grayscale(image, size=(8, 8)):
"""将图像缩放并转换为灰度图。"""
resized = cv2.resize(image, size)
gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
return gray
def compute_hash(image, size=(8, 8)):
"""计算图像的感知哈希值。"""
gray = resize_and_grayscale(image, size)
mean = np.mean(gray)
binary = (gray > mean).astype(int)
return binary.flatten()
def compute_hamming_distance(hash1, hash2):
"""计算两个哈希值之间的汉明距离。"""
xor = np.bitwise_xor(hash1, hash2)
return np.sum(xor)
def load_images_from_folder(folder, extensions=('jpg', 'jpeg', 'gif', 'png', 'bmp')):
"""从指定文件夹加载图像,并返回图像文件名和哈希值的列表。"""
images = []
for ext in extensions:
images.extend(glob.glob(f'{folder}/*.{ext}'))
hashes = {img_path: compute_hash(cv2.imread(img_path)) for img_path in images}
return hashes
# ------------------ 主程序 ------------------
def main():
# 设置检索图像路径
query_image_path = "img_2.png"
query_image = cv2.imread(query_image_path)
if query_image is None:
print(f"Error: Unable to load query image {query_image_path}")
return
query_hash = compute_hash(query_image)
print(f"检索图像的感知哈希值为:\n{query_hash}")
# 加载指定文件夹下的所有图像及其哈希值
image_hashes = load_images_from_folder('img')
# 找出最相似的图像
distances = [(compute_hamming_distance(query_hash, image_hash), image_path)
for image_path, image_hash in image_hashes.items()]
sorted_distances = sorted(distances)
# 绘制结果
fig, axs = plt.subplots(1, 4, figsize=(12, 4))
axs[0].imshow(cv2.cvtColor(query_image, cv2.COLOR_BGR2RGB))
axs[0].set_axis_off()
axs[0].set_title("Query Image")
for i, (distance, image_path) in enumerate(sorted_distances[:3], start=1):
image = cv2.imread(image_path)
axs[i].imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
axs[i].set_axis_off()
axs[i].set_title(f"Result {i} - Distance: {distance}")
plt.tight_layout()
plt.show()
if __name__ == "__main__":
main()
标签:搜图,image,cv2,OpenCV,以图,哈希,图像,query,path
From: https://blog.csdn.net/summerriver1/article/details/137025000