Python图像处理【6】通过哈希查找重复和类似的图像

news2024/12/27 12:47:46

通过哈希查找重复和类似的图像

    • 0. 前言
    • 1. 哈希函数
    • 2. 使用哈希函数查找重复图像
    • 3. 使用感知哈希函数查找相似图像
      • 3.1 感知哈希函数
      • 3.2 查找相似图像
      • 4.3 查找指定目录中的相似图像
    • 小结
    • 系列链接

0. 前言

在本节中,我们将讨论图像搜索中的两个相关问题,即使用基于哈希函数的方法来解决问题图像搜索问题,包括查找重复图像和查找类似图像,但要解决这两个问题所用的哈希函数在本质上是截然不同的:

  • 第一个问题是在给定的图像集合中找到重复图像,我们将学习如何使用 MD5 加密哈希函数来解决该问题
  • 第二个问题是如何查找与给定图像相似图像,我们将学习如何使用感知哈希函数来解决此问题

1. 哈希函数

哈希函数 (Hash Function) 可以将任意长度的数据字符串映射到固定长度的输出,哈希函数本身具有确定性和公开性,但是映射结果应该看起来似乎是“随机”的,函数表达式为 y=hash(key)。在实践中,哈希函通常用于压缩输入数据。哈希函数应当具有抗碰撞性,也就是说,对于两个输入 M1M2,设计良好的哈希函数应当满足 h ( M 1 ) ≠ h ( M 2 ) h(M1)≠h(M2) h(M1)=h(M2)
MD5 (Message Digest 5) 是一种广泛使用的哈希函数,该函数可以生成 128 位哈希值。即该算法可以接受任意长度的输入,并得到长度为 128 位的输入结果。尽管 MD5 最初设计用作加密哈希函数,现在已证明它具有脆弱性,但该算法仍被用于验证文件的完整性和真实性。MD5 相较于其他加密哈希函数的优势在于,它的执行更为高效。在下一小节中,我们将介绍如何使用 hashlib 检测重复图像。

2. 使用哈希函数查找重复图像

我们使用图像内容作为 MD5 哈希函数的键 (key),然后计算图像集合中所有图像的十六进制哈希值( 128 位)。如果集合中存在彼此相同的图像,它们将具有相同的十六进制哈希值。通过比较哈希值,我们可以判断两张图像的内容是否相同。
但是,MD5 容易受到碰撞性的影响,这意味着有可能即使两个图像并不相同,它仍然有可能会产生相同的哈希值,但这种情况在哈希空间较大时极为罕见。

(1) 首先,导入所需的库:

import hashlib, os
from glob import glob
import matplotlib.pylab as plt
from skimage.io import imread

hashlib 模块实现了 RSAMD5 哈希算法接口,其使用方法如下,创建以图像内容作为 MD5 对象的键,并计算哈希值。给定一个任意长度(即,对于任何大小的图像)的键,MD5 哈希函数将返回具有固定长度( 128 位)的哈希值。

(2) 计算图像的十六进制哈希值,然后计算位长度:

hex_digest = hashlib.md5(open('1.png', 'rb').read()).hexdigest()
bin_digest = format(int(str(hex_digest), 16), "040b")
print('MD5 digest = {} ({})'.format(hex_digest, bin_digest) )
print('length of hex digest = {} bytes'.format(len(hex_digest)))
print('length of bin digest = {} bits'.format(len(bin_digest)))
# MD5 digest = 284e09c26262ba709b9c54016c3ee197 (101000010011100000100111000010011000100110001010111010011100001001101110011100010101000000000101101100001111101110000110010111)
# length of hex digest = 32 bytes
# length of bin digest = 126 bits

从以上输出可以看出,哈希值的长度为 128 位。

(3) 接下来,我们实现函数 find_duplicates,该函数以目录名称作为输入,获取目录下的所有图像(带有 .jpg.png 扩展名),然后返回找到的重复图像列表,每个列表包含两个或多个内容相同的图像名:

def find_duplicates(dir_name):
    def is_image(file_name):
        f = file_name.lower()
        return f.endswith(".png") or f.endswith(".jpg")

    hash_keys = dict()
    for file_name in glob(dir_name):
        if os.path.isfile(file_name) and is_image(file_name):
            with open(file_name, 'rb') as f:
                file_hash = hashlib.md5(f.read()).hexdigest()
            if file_hash not in hash_keys:
                hash_keys[file_hash] = [file_name]
            else:
                hash_keys[file_hash].append(file_name)
    return [hash_keys[file_hash] for file_hash in hash_keys if len(hash_keys[file_hash]) > 1]

在函数 find_duplicates() 中,计算所有图像的十六进制哈希值,使用 Python 词典将重复的项目插入其中,字典的键是计算出的十六进制摘要,其值是具有给定的十六进制哈希值的文件名列表。如果字典中已经存在一个具有相同的十六进制键,则表示它是一个重复的图像,我们将文件名添加到该键的相应列表中。最后,返回具有多个文件名的键值对列表作为重复图像

(4) 定义函数 show_duplicates 显示所有重复的图像,函数在 find_duplicates 返回的列表上迭代,在同一行上打印重复图像,并计算重复的次数:

def show_duplicates(duplicates):
    for duplicated in duplicates:
        try:
            plt.figure(figsize=(20,10))
            plt.subplots_adjust(0,0,1,0.9,0.05,0.05)
            for (i, file_name) in enumerate(duplicated):
                plt.subplot(1, len(duplicated), i+1)
                plt.imshow(imread(file_name))
                plt.title(file_name, size=10)
                plt.axis('off')
            plt.suptitle('{} duplicate images found with MD5 hash'.format(len(duplicated)), size=15)
            plt.show()
        except OSError as e:
            continue

(5) 最后,调用以上函数查找重复图像,并进行显示:

duplicates = find_duplicates('images/*.*')
print(duplicates)
show_duplicates(duplicates)

重复图像
重复图像

3. 使用感知哈希函数查找相似图像

在本节中,我们将学习如何通过使用图像哈希查找与给定图像相似的图像,我们使用感知哈希函数 (perceptual Hash function, pHash) 来实现此目的。

3.1 感知哈希函数

传统加密哈希算法(例如 MD5 )的关键特征抗碰撞性,即使输入有一点变化也会显着改变输出结果。使用加密哈希,哈希值是随机的(通常也可以认为是伪随机的),用于生成哈希值的数据可以视为随机种子,因此,相同的输入数据可以生成相同哈希值,但是不同的数据将会产生完全不同的哈希值。
因此,如果对图像执行了某些操作(例如压缩、裁剪和缩放),哈希值将变得完全不同,即使这些图像在视觉上具有相似的显示效果。为了查找内容相似的的图像,图像哈希函数应考虑图像视觉变化,并根据图像的视觉效果生成哈希值,这种类型的哈希函数可以用于识别集合中是否存在与给定图像相似的的图像。
感知哈希算法是一类可比的哈希函数,使用图像中的特征生成不同的(但不唯一的)指纹,这些指纹具有可比性。换句话说,如果特征相似,则感知哈希函数值也是类似的,而密码哈希函数对于输入值的微小变化极为敏感(会导致输出值的剧烈变化)。pHash 使用一种鲁棒性算法,即离散余弦变换来降低频率。

3.2 查找相似图像

在本节中,使用 imagehash 库的 pHash 算法计算相似图像,我们使用数据集 caltech-101。为了使用 pHash 函数,需要首先安装第三方库 imagehash

$ pip install imagehash

(1) 首先导入所需的库:

from PIL import Image
import imagehash
from time import time
import os
from glob import glob
import matplotlib.pylab as plt
import numpy as np

(2) 实现函数 plot_images_to_compare(),其接受两个输入图像和一个哈希函数(默认为 pHash )作为参数,使用哈希函数计算每个输入图像的 64 位指纹,并绘制相似图像;最后,计算图像的指纹之间的汉明距离:

def plot_images_to_compare(imfile1, imfile2, hashfunc = imagehash.phash):
    img1, img2 = Image.open(imfile1), Image.open(imfile2)
    print('sizes of images = {}, {}'.format(img1.size, img2.size))
    hash1 = hashfunc(img1)
    hash2 = hashfunc(img2)
    plt.figure(figsize=(20,10))
    plt.subplots_adjust(0,0,1,0.95,0.01,0.01)
    plt.subplot(121), plt.imshow(img1), plt.title(str(hash1), size=10), plt.axis('off')
    plt.subplot(122), plt.imshow(img2), plt.title(str(hash2), size=10), plt.axis('off')
    plt.show()
    print('hash1 = {} ({}), length = {} bits'.format(format(int(str(hash1), 16), "040b"), str(hash1),                                                      len(format(int(str(hash1), 16), "040b"))))
    print('hash2 = {} ({}), length = {} bits'.format(format(int(str(hash2), 16), "040b"), str(hash2),                                                      len(format(int(str(hash2), 16), "040b"))))
    print('hamming distance =', hash1 - hash2)

(3) 调用函数 plot_images_to_compare() 比较两个不同的图像,其中第二张图像可以通过在第一个中添加一些随机笔画创建:

plot_images_to_compare('bird_01.jpg', 'bird_02.jpg')
# sizes of images = (300, 258), (300, 258)
# hash1 = 1001101101001000011001110110011010010100100110011011001101100011 (9b4867669499b363), length = 64 bits
# hash2 = 1001101101001000011001110110011010010100100110011011001101100011 (9b4867669499b363), length = 64 bits
# hamming distance = 0

图像比较
根据输出可以看出图像非常相似,两者会返回完全相同的 pHash指纹,它们之间的汉明距离为 0

(4) 接下来,我们计算原始输入图像与使用图像增强后的 pHash 值:

plot_images_to_compare('bird_01.jpg', 'bird_03.png')
# sizes of images = (300, 258), (300, 258)
# hash1 = 1001101101001000011001110110011010010100100110011011001101100011 (9b4867669499b363), length = 64 bits
# hash2 = 1001101101001000011001110110011010010100100110011101001101100011 (9b4867669499d363), length = 64 bits
# hamming distance = 2

图像比较
可以看出,两张图像同样非常相似,汉明距离为 2,即 pHash 指纹仅有 2 位不同。

(5) 对比原始图像与添加水印后图像(图像的大小尺寸不同)的 pHash 指纹:

plot_images_to_compare('img_with_logo.jpg', 'img_with_no_logo.png')
# sizes of images = (1024, 683), (574, 383)
# hash1 = 1001010110000001011010111101001010010000111100100111001001111110 (95816bd290f2727e), length = 64 bits
# hash2 = 1001010110000001011010101101001010010010111100100111001001111110 (95816ad292f2727e), length = 64 bits
# hamming distance = 2

图像比较
从以上输出可以看出中,指纹同样仅有 2 位不同。

(6) 最后,我们比较两个完全不同图像的 pHash 指纹:

plot_images_to_compare('bird_01.jpg', 'img_with_no_logo.png')
# sizes of images = (300, 258), (574, 383)
# hash1 = 1001101101001000011001110110011010010100100110011011001101100011 (9b4867669499b363), length = 64 bits
# hash2 = 1001010110000001011010101101001010010010111100100111001001111110 (95816ad292f2727e), length = 64 bits
# hamming distance = 28

图像比较

可以看到,两张图像的指纹差异较大,大约有一半左右的位上值并不相同。

4.3 查找指定目录中的相似图像

(1) 实现函数 preprocess_images(),以处理指定目录中的图像,为每一图像生成 pHash 指纹:

def preprocess_images(dir_name, hashfunc = imagehash.phash):    
    image_filenames = sorted(glob(dir_name))
    print('number of images to process = {}'.format(len(image_filenames)))
    images = {}
    for img_file in sorted(image_filenames):
        hash = hashfunc(Image.open(img_file))
        images[hash] = images.get(hash, []) + [img_file]
    for hash in images:
        images[hash] = np.array(images[hash])
    return images 

在函数 preprocess_images() 中,创建一个字典,其每一个键都对应于唯一的指纹(哈希值),相应的值是具有相同指纹值的图像列表,函数返回图像指纹字典。

(2) 实现函数 query_k_similar_images(),其接受待查询的图像文件名、要搜索的图像指纹字典、希望获取的与待查询图像最相似的图像数量K以及要使用的哈希函数(默认为 pHash):

def query_k_similar_images(image_file, images, k=3, hashfunc = imagehash.phash):
    hash = hashfunc(Image.open(image_file))
    hamming_dists = np.zeros(len(images))
    image_files = np.array(list(images.values()))
    hash_values = list(images.keys())
    for i in range(len(image_files)):
        hamming_dists[i] = hash - hash_values[i]
    indices = np.argsort(hamming_dists)
    return np.hstack(image_files[indices][:k]), hamming_dists[indices][:k]

函数 query_k_similar_images() 首先计算待查询图像的指纹,然后计算待查询图像的指纹与集合中其他指纹之间的汉明距离;最后,按照汉明距离递增顺序对图像进行排序,并返回前 K 张最相似的图像。

(3) 预处理所有输入图像,并计算图像预处理所需时间:

start = time()
images = preprocess_images('lamp/*.*')
end = time()
print('processing time = {} seconds'.format(end-start))

(3) 定义函数 plot_query_returned_images() 以绘制待查询图像以及函数返回的相似图像:

def plot_query_returned_images(query, returned):
    n = 1 + len(returned)
    plt.figure(figsize=(20,8))
    plt.subplots_adjust(0,0,1,0.95,0.05,0.05)
    plt.subplot(1,n,1), plt.imshow(Image.open(query)), plt.title('query image', size=20), plt.axis('off')
    for i in range(len(returned)):
        plt.subplot(1,n,i+2), plt.imshow(Image.open(returned[i])), plt.title('returned image {}'.format(i+1), size=10)
        plt.axis('off')
    plt.show()

(4) 查找与待查询图像的前四个最相似图像进行绘制,并返回图像指纹之间的汉明距离:

query = 'lamp/image_0001.jpg'
found, dists = query_k_similar_images(query, images, k=4)

plot_query_returned_images(query, found)

查找相似图像

小结

哈希函数 (Hash Function) 可以将任意长度的数据字符串映射到固定长度的输出。在本节中,我们利用哈希函数学习了图像搜索中的两个相关问题,即使用基于哈希函数的方法查找重复图像和查找类似图像,对于第一个问题,我们学习了使用 MD5 加密哈希函数来解决,对于第二个问题,我们介绍了感知哈希函数来解决。

系列链接

Python图像处理【1】图像与视频处理基础
Python图像处理【2】探索Python图像处理库
Python图像处理【3】Python图像处理库应用
Python图像处理【4】图像线性变换
Python图像处理【5】图像扭曲与逆扭曲详解

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

node.js+uni计算机毕设项目贵州苗族文化展播微信小程序(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置: Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等…

IP多播(计算机网络-网络层)

目录 一对多通信的应用需求 单播 vs 多播 多播路由器(Multicast Router) IP 多播的一些特点 D 类 IP 地址与以太网多播地址的映射关系 IP多播需要两种协议 互联网组管理协议 IGMP 多播路由选择协议 两种多播路由选择方法 建议的IP多播路由选择协…

后渗透操作(一)

在通过木马或者漏洞获得靶机的远程控制权后接下来就是后渗透的过程了,meterpreter可以看作一个支持多操作平台的高级后门工具,可以实现特权提升、信息攫取、系统监控、跳板攻击与内网拓展等多样化的功能特性。 一、访问文件系统 对于文件系统的访问有如…

腾讯云轻量应用服务器使用Typecho 应用镜像搭建博客!

Typecho 是开源的博客建站平台,具有轻量、高效、稳定等特点,操作界面简洁友好。该镜像基于 CentOS 7.6 64 位操作系统,并已预置 Nginx、PHP、MariaDB 软件。您可以使用它快速搭建博客、企业官网、电商、论坛等各类网站。 操作步骤 登录 轻量…

博士毕业论文答辩PPT的基本要点

博士毕业论文答辩PPT的基本要点 有借鉴和参考价值的地址或链接: 1、https://blog.csdn.net/lwz45698752/article/details/106648720 研究背景及研究意义 研究内容 总结以研究成果 里边的配色、结果展示的方式很值得借鉴,看起来重点突出&#xff0c…

【Pandas入门教程】如何轻松处理时间序列数据

如何轻松处理时间序列数据 来源:Pandas官网:https://pandas.pydata.org/docs/getting_started/intro_tutorials/index.html 笔记托管:https://gitee.com/DingJiaxiong/machine-learning-study 文章目录如何轻松处理时间序列数据导包数据准备【…

RV1126笔记十八:吸烟行为检测及部署<五>

若该文为原创文章,转载请注明原文出处。 ubuntu16.04上搭建转化成RKNN环境并把onnx转成RKNN模型(ubuntu16.04) onnx模型转rknn模型需要用到py3.8,所以搭建环境在转换,下面全部操作都是虚拟机下进行。 注意:转模型用py3.6, 训练用py3.8 一、miniconda安装 1、下载地址…

tensorflow01——安装,结构

从官网安装anaconda 安装tensorflow 注意tensorflow对应特定的python版本如3.6,3.7 直接从官网下载的anaconda会对应下载最新版本的python3.9 所以需要新建一个环境来装我们的tensorflow和它对应的python (macbook m2) 01创建新的环境命名…

【详细说明】二代身份证号码的组成结构(含校验码算法与行政区划代码)

文章内容:二代身份证号码的组成结构(含校验码算法与行政区划代码) 关键词组:身份证号码、组成、校验码、行政区划码 使用软件:无 虚拟环境:无 操作系统:Windows 11 【图源中国政府网】 文章目录…

node.js+uni计算机毕设项目广播剧微信小程序(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置: Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等…

Node.js - 模块化

文章目录目标一、模块化的基本概念1、什么是模块化(1)现实生活中的模块化(2)编程领域中的模块化2、模块化规范二、Node.js 中模块化1、Node.js 中模块的分类2、加载模块3、Node.js 中的模块作用域(1)什么是…

创新!京东T7开创“新算法宝典”,图文并茂,全新演绎,太酷了

导言 算法是一门学问,但却总遭到一些程序员的冷落。现在的开发人员,更热衷于编程语言的修炼,以应付面试需求时的需要,所以对算法的学习,稍许忽略了些。实际上,近些年来,各互联网公司对于算法的…

画出一阶系统单位阶跃响应、单位脉冲响应、单位斜坡响应曲线

画出一阶系统单位阶跃响应、单位脉冲响应、单位斜坡响应曲线 t0:0.1:7; num[1]; den[1 1]; figure; c1impulse(num,den,t); plot(t,c1,‘b-’); xlabel(‘t/s’);ylabel(‘c(t)’);grid on; figure; c2step(num,den,t); plot(t,ones(size(t)),‘r-’,t,c2,‘b-’); xlabel(‘t…

Java中File文件操作类的详细使用介绍

文章目录File类的使用File的介绍File常用API判断文件类型-获取文件信息创建文件-删除文件功能遍历文件夹File类的使用 File的介绍 File的概述: File类在包java.io.File下、代表操作系统的文件对象(文件、文件夹)。 File类提供了诸如:定位文…

神级框架 - MyBatis【进阶】

目录 1. 单表查询的进阶知识 1.1 参数占位符 #{} 和 ${} 的区别 1.1.1 #{} 和 ${} 的区别一 (#{} 胜一分) 1.1.2 #{} 和 ${} 的区别二 (${} 胜一分) 1.1.3 #{} 和 ${} 的区别三 - 最主要的区别 (${} 惨败) 1.2 like 查询 2. 多表查询的进阶知识 2.1 查询的返回类型: res…

node.js+uni计算机毕设项目基于微信平台的小龙虾养殖管理程序设计(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置: Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等…

【Lilishop商城】No4-1.业务逻辑的代码开发,涉及到:会员B端第三方登录使用及后端接口(微信、QQ等)

仅涉及后端,全部目录看顶部专栏,代码、文档、接口路径在: 【Lilishop商城】记录一下B2B2C商城系统学习笔记~_清晨敲代码的博客-CSDN博客 全篇会结合业务介绍重点设计逻辑,其中重点包括接口类、业务类,具体的结合源代…

Mycat2(二)windows搭建mycat2、mycat2相关概念、配置文件解释

文章目录windows搭建mycat2步骤下载mycat2修改配置并启动mycat2命令mycat2相关概念mycat2配置文件用户相关配置属性数据源datasources集群cluster相关配置逻辑库表schemas配置windows搭建mycat2步骤 修改C:\Windows\System32\drivers\etc下的hosts文件,防止dns污染…

node.js+uni计算机毕设项目基于微信小程序某企业考勤系统(程序+小程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置: Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分离等…

「设计模式」享元模式

「设计模式」享元模式 文章目录「设计模式」享元模式[toc]一、概述二、结构三、案例实现四、优缺点五、JDK中的享元模式六、小结一、概述 在面向对象程序设计过程中,有时会面临要创建大量相同或相似对象实例的问题。创建那么多的对象将会耗费很多的系统资源&#x…