感知哈希-图片相似度分析

news2024/10/7 10:25:39

‍本文作者是360奇舞团开发工程师

引言

最近在做小程序换肤功能,因为不同主题色的小程序对应了不同图片库,项目内图片引用的方式又是线上URL地址配置形式,新加一套图片时,就要将图片和线上URL链接对比之后,配置到对应的Key上。这么人工操作一遍后发现费时费力。因为不同图片库间主要是颜色差异,想着能不能通过图片相似度对比以后,自动将图片匹配对应的Key。最后研究了下,通过感知哈希实现了我的需求,记录一下。

感知哈希

概念

感知哈希是使用指纹算法生成各种形式的多媒体的片段、哈希或指纹。感知哈希是一种局部敏感哈希,如果多媒体的特征相似,则它是类似的
在图像应用中,感知图像哈希是一种根据图像的视觉外观创建图像指纹的方法。这种指纹使得比较相似的图像变得更加容易。这种算法常用于以图搜图场景,根据提供图片返回视觉上相似的图片,比如谷歌的图像搜索就基于感知哈希。

感知哈希与加密哈希

与 MD5 和 SHA1 等加密哈希函数相比,感知哈希是一个不同的概念
对于加密哈希,哈希值是随机的。用于生成哈希的数据就像随机种子一样,因此相同的数据将生成相同的结果,但不同的数据将产生截然不同的结果。相反,感知哈希可以进行比较——得到两个数据集之间的相似性
比较两个 SHA1 哈希值实际上只能得到两个结论。如果哈希值不同,则数据不同。如果哈希值相同,则数据可能相同。(由于存在哈希冲突的可能性,因此具有相同的哈希值并不能保证相同的数据)。
基于此特性,MD5
SHA1
可以用来计算文件的哈希值,再进行对比,来判断是否是重复文件。但是对于图片相似度场景来说,对数据相同判定如此严苛的对比算法就不太适用。因为,对于图片来说,图像格式、元数据等等信息的修改,都会导致像素内容完全相同的情况下却有不同的二进制内容,从而导致哈希截然不同。
444106f58672f5f5fadd02836f93ae70.png
除此之外,图片的分辨率、亮度、色度、对比度、模糊度、缩放、旋转、截取、小幅度修改等操作导致的相似图片,使用加密哈希的方式对比,会得到完全不同结果。因此,图片相似度需要的不是简单粗暴的对比文件的二进制内容,而是图片上像素的色彩分布方式。在感知哈希计算相似性的场景下,大致可以理解为,图像颜色直方图中最右侧的白色色阶和最左侧的黑色色阶在图片中的分布情况。

汉明距离

通过感知哈希可以为图片生成一个指纹字符串,通过比较字符串的距离(通常采用汉明距离,Hamming distance)这个距离越小,代表两个图片越相似,一般的,我们有以下规则:
:::tips
Hamming distance = 0 -> particular like
Hamming distance < 5 -> very like
hamming distance > 10 -> different picture
:::
汉明距离是使用在数据传输差错控制编码里面的,汉明距离是一个概念,它表示两个(相同长度)字符串对应位置的不同字符的数量。对两个字符串进行异或运算,并统计结果为1的个数,那么这个数就是汉明距离
在信息论中,两个等长字符串之间的汉明距离是两个字符串对应位置的不同字符的个数。换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字符个数。例如:
101100 与 111000 之间的汉明距离是 2
21438 与 22337 之间的汉明距离是 3。
"toned" 与 "roses" 之间的汉明距离是 3。
682978d4993b77ad90cda71a0a7399f4.png

实现过程

感知哈希算法是一类算法的总称,根据色彩分布方式的计算方式不同,常用的感知哈希算法,包括aHash、pHash、dHash
实现的过程可以简化为:简化图片-获取像素值-计算图像哈希。不同的哈希算法都有相同的基本特性:对于图像的放大或缩小、不同纵横比,较小颜色差异(对比度、亮度、模糊、饱和度等)仍然会匹配为相似图像
平均哈希算法(aHash):
该算法是基于比较灰度图每个像素与平均值来实现。
91a511cff35c6c2ea640c48734fc8147.png
海湾大桥夜景 – 照片来源:DH Parks ( CC )
下面展示用python计算平均哈希的过程:
第一步是使用PILPillow减小图像的大小和颜色。这样做是为了降低图像的复杂性,从而使比较更加准确。

image = image.resize((8, 8), Image.ANTIALIAS)  # Reduce it's size.

a32f9e093f65623681044700960b0465.png

image = image.convert("L")  # Convert it to grayscale.

c6236c6eab39556e67171f08f580a633.png
接下来我们求图像的平均像素值:

pixels = list(image.getdata())
avg = sum(pixels) / len(pixels)

pixels只是一个像素值列表,范围从 0(黑色)到 255(白色),我们只需将它们相加并除以数量即可得到平均值。对于该图像,平均像素值为 61(大约 25% 灰度)。
现在我们可以计算哈希值了。这是通过将图像中的每个像素与平均像素值进行比较来完成的。如果像素值小于平均值,则为 0;如果大于平均值,则为 1。然后我们将其视为一串位并将其转换为十六进制。

bits = "".join(map(lambda pixel: '1' if pixel &lt; avg else '0', pixels))  # '00010100...'
hexadecimal = int(bits, 2).__format__('016x').upper()

a4d0a9dad945b68cf1bef4c4809ee93d.png
转换为黑白图像的位。
这就可以得到一个哈希值 00010E3CE08FFFFE,可用于将该图像的“结构”(looks)与使用相同方式哈希的任何其他图像进行汉明距离比较。距离越接近 0,图像越相似,0 表示基本相同。
感知哈希算法(pHash):
pHash算法即感知哈希算法,原理是通过离散余弦变换(DCT)降低图片频率,在图像频率中,高频代表细节,低频代表结构。通过有损压缩的方式保留大部分图像特征,再对特征值进行比较,即忽略高频保留低频
DCT:DCT全称为Discrete Cosine Transform,即离散余弦变换。其原则与傅里叶变换相近,都是把目标信号从复杂的时域信号,分解为不同频率强度的频域信号。由傅里叶的原理可知,复杂信号是简单信号的叠加,所以DCT的基本原理就是,通过多个不同强度不同频率的DCT基信号,即可叠加“拼装”为原本的信号,因此实际记录的时候就不再需要记录复杂的原始信号,而是记录DCT的基即可。并且信号转为DCT的频域信号后,左上角表达了其低频信号强度,右下角表达了其高频信号强度,使得信号的频率分量和强度一目了然,没有原始信号的纷繁复杂。
DCT矩阵中从左上角到右下角代表越来越高频率的系数,但是除左上角外,其他部分的系数都为0左右,pHash算法在简化图片时常常将图片缩小为3232,因此只选取DCT矩阵中左上角88的部分即可得到图像的大部分特征。
b08af0dc178b26b6a51b5fd1d199acb7.png
再将88的部分矩阵的每个值与均值比较,组合得到的同样是一个64位的0/1哈希序列,最终再通过同位对比得到相似度大小。
pHash算法优点是更为稳定,判断效果好,但速度略慢
差异哈希算法(dHash):
dHash算法即差异哈希算法,原理是比较每行相邻元素的大小,如果左边的像素比右边的像素更亮则标记为1,否则为0,最后组合得到哈希序列
这种算法在简化图片时常常将图片缩小为9
8,每行9个元素相邻比较可得到8个值,一共8行,结果也是一个64位的0/1哈希序列。
两张图片的哈希序列同样通过同位对比即可得到相似度大小。
dHash算法的优点是速度快,同时判断效果要好于aHash。
上面的三种hash算法可以用opencv实现,网上有很多代码实现,这里就不贴了。也可以直接使用python中的三方库imagehash,都有封装实现,使用也很简单。

总结

图像相似度对比有很多场景,了解感知哈希算法对比图片相似度的原理,才能搞清楚它适用于哪一种。感知哈希提供了一种简单方法,对于图片轻微调色、缩放、甚至有较小细节差异等场景,在多数情况下都有较高的识别率。而像裁剪、旋转、添加局部矫正(边框、水印)这种影响了图像色彩分布结构的场景就会有比较高的识别错误率。
没有完全百分之百正确率的相似度图像算法,因为图像相似度差异可以是千差万别,相似度又是个相对概念,即使是人也会有判断错误的时候。所以,只要根据自己实际的使用场景,采用其它一些辅助识别的算法做兼容,满足自己的需求即可。

参考链接

https://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
https://web.archive.org/web/20171112054354/https://www.safaribooksonline.com/blog/2013/11/26/image-hashing-with-python/
https://blog.csdn.net/cjzjolly/article/details/123524616
https://zhuanlan.zhihu.com/p/68215900
https://www.yumefx.com/?p=3163

- END -

关于奇舞团

奇舞团是 360 集团最大的大前端团队,代表集团参与 W3C 和 ECMA 会员(TC39)工作。奇舞团非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。

e36ccea673d94f55035a7fe0cdb0d4ae.png

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

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

相关文章

linux日志轮转工具logrotate

目录 一、日志轮转工具的由来 二、如何去使用logrotate工具 2.1 使用cron驱动logrotate 2.2 使用systemd的timer驱动logrotate 三、logrotate命令的子命令解析 四、logrotate的配置 4.1 配置文件的位置 4.2 配置项的具体含义 一、日志轮转工具的由来 在Linux环境中能够…

代码随想录算法训练营第48天 | ● 198.打家劫舍 ● 213.打家劫舍II ● 337.打家劫舍III

文章目录 前言一、198.打家劫舍二、213.打家劫舍II三、337.打家劫舍III总结 前言 dp[]; 一、198.打家劫舍 仔细一想&#xff0c;当前房屋偷与不偷取决于 前一个房屋和前两个房屋是否被偷了。 动规五部曲分析如下&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下…

【Java|golang】210. 课程表 II---拓扑排序

一、拓扑排序的定义&#xff1a; 先引用一段百度百科上对于拓扑排序的定义&#xff1a; 对一个有向无环图 ( Directed Acyclic Graph 简称 DAG ) G 进行拓扑排序&#xff0c;是将 G 中所有顶点排成一个线性序列&#xff0c;使得图中任意一对顶点 u 和 v &#xff0c;若边 <…

Mybatis-plus的QueryWrapper的函数,常见方法

获取id 有时候我们新建一条数据的时候要生成一个新的id&#xff0c;我们可以通过下面的类获取 IdWorker.getId()mybatis-plus同时存在and和or查询 LambdaQueryWrapper<House> queryWrapper new QueryWrapper<House>().lambda(); queryWrapper.eq(House::getTena…

安果相亲-找到心仪的另一半 一个安卓免费找对象软件推荐

安果相亲&#xff1a;全国范围内的真实恋爱相亲平台&#xff01; 致力于帮助用户寻找真实恋爱&#xff0c;我们的实名认证机制确 保用户信息的真实性。汇集了高学历、经济稳定、丰富生活经验的优质单身男女&#xff0c;都在这里真诚地等待那个对的人。每个手机只能注册一个账户…

数据结构:线性表(队列实现)

1. 队列的概念及结构 队列:只允许在一端进行插入数据操作,在另一端进行删除操作的特殊线性表,队列具有先进先出(FIFO)的特性. 进行插入操作的一端称为队尾;进行删除操作的一端叫做队头 队列应用于 解决公平性排队(抽号机)广度优先遍历(BFS) 2. 队列的定义 和栈一样,队列也可…

【知网检索】第三届教育,语言与艺术国际学术会议(ICELA 2023)

第三届教育&#xff0c;语言与艺术国际学术会议(ICELA 2023) The 3rd International Conference on Education, Language and Art 第三届教育&#xff0c;语言与艺术国际学术会议&#xff08;ICELA 2023&#xff09;将于2023年11月17-19日在中国北京召开。会议主要围绕会议主…

电力系统知识预备及学习方向

由于电源点与负荷中心多数处于不同地区&#xff0c;也无法大量储存&#xff0c;故其生产、输送、分配和消费都在同一时间内完成&#xff0c;并在同一地域内有机地组成一个整体&#xff0c;电能生产必须时刻保持与消费平衡。因此&#xff0c;电能的集中开发与分散使用&#xff0…

explainerdashboard,一个神奇的 python 库

今天给大家分享一个神奇的 python 库&#xff0c;explainerdashboard。 https://github.com/oegedijk/explainerdashboard explainerdashboard 是一种用户友好的工具&#xff0c;可以简化复杂的机器学习模型&#xff0c;解释&#xff08;与 scikit-learn 兼容&#xff09;机器…

Bodhi Linux 7.0发布:全新亮点抢先看

导读我们非常高兴地宣布Bodhi Linux 7.0的发布&#xff0c;这是一个小型开发团队经过数月的专注开发所取得的成果。从性能增强到尖端更新&#xff0c;让我们深入了解这个版本&#xff0c;看看它为那些寻求轻量级Linux桌面体验的用户带来了什么新特性。 Bodhi Linux 7.0桌面 Bo…

12.(Python数模)(相关性分析一)相关系数矩阵

相关系数矩阵 相关系数矩阵是用于衡量多个变量之间关系强度和方向的统计工具。它是一个对称矩阵&#xff0c;其中每个元素表示对应变量之间的相关系数。 要计算相关系数矩阵&#xff0c;首先需要计算每对变量之间的相关系数。常用的相关系数包括皮尔逊相关系数和斯皮尔曼相关…

第32章 Linux内核打印实验

本手册的实验都是在buildroot系统上完成的&#xff0c;由于buildroot系统已经设置了相应的打印等级&#xff0c;所以驱动的相关打印都能正常显示在串口终端上&#xff0c;如果将实验系统换成了ubuntu&#xff0c;然后加载同样的驱动&#xff0c;会发现打印信息不见了&#xff0…

PBR纹理的10种贴图

PBR 是基于物理的渲染的首字母缩写。它试图通过模拟材料如何吸收和反射光&#xff0c;以模仿现实世界中的光流的方式产生视觉效果。最近的游戏引擎由于其逼真的效果而越来越多地使用 PBR 纹理。对于实时渲染&#xff0c;它们被认为是真实世界场景的最佳近似值。 推荐&#xff…

极星 4:一辆不需要宣传就自带美学的车

当市面上的新车都开始逐步走向更多的功能、更繁琐的设计的时候&#xff0c;极星更像一个独行的人&#xff0c;走了一条跟其他车企大相径庭的道路&#xff0c;带给我们对于同一个世界不同的视图。 不张扬、够简洁&#xff0c;从不炫耀自己但又蕴含着大的智慧&#xff0c;如夜空中…

Python深度学习-Keras》精华笔记4:解决深度学习回归问题

公众号&#xff1a;尤而小屋作者&#xff1a;Peter编辑&#xff1a;Peter 持续更新《Python深度学习》一书的精华内容&#xff0c;仅作为学习笔记分享。 本文是第4篇&#xff1a;基于Keras解决深度学习中的回归问题。 Keras内置数据集 回归问题中使用的是内置的波士顿房价数据…

JavaScript中的事件捕获(event capturing)和事件冒泡(event bubbling)

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 事件捕获和事件冒泡⭐ 事件捕获&#xff08;Event Capturing&#xff09;示例&#xff1a; ⭐ 事件冒泡&#xff08;Event Bubbling&#xff09;示例&#xff1a; ⭐ 应用场景⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开…

苹果电脑版虚拟机推荐 VMware Fusion Pro for mac(vm虚拟机)

VMware Fusion Pro是一款功能强大的虚拟化软件&#xff0c;专为Mac用户设计。它允许用户在Mac上创建、运行和管理虚拟机&#xff0c;以便同时运行多个操作系统和应用程序。 以下是VMware Fusion Pro的一些主要特点和功能&#xff1a; 1. 多操作系统支持&#xff1a;VMware Fu…

CocosCreator3.6.2图片导入到工程,没办法拖动到场景中

解决方案&#xff1a;将资源的属性类型由texture调整为sprite-frame

字节、华为、美团软件测试面试真题(超详细~)

前言 最近已经算是秋招了&#xff0c;所以最近博主会努力给大家搜集整理一些各大公司测试岗测开岗的面经&#xff0c;希望能帮助到大家更好的入职想去的公司哦&#xff0c;关注我&#xff0c;一个每日分享软件测试知识的日更博主。 同时&#xff0c;我也准备了一份软件测试面…