Iconfinder 如何杜绝盗版,哈希算法检测图像重复
原地址:http://blog.jobbole.com/65914/
本文由 伯乐在线 - 小鱼 翻译自 Silviu Tantos。欢迎加入技术翻译小组。转载请参见文章末尾处的要求。
【伯乐在线导读】:Iconfinder 是一个图标搜索引擎,为设计师、开发者和其他创意工作者提供精美图标,目前托管超过 34 万枚图标,是全球最大的付费图标库。用户也可以在 Iconfinder 的交易板块上传出售原创作品。每个月都有成千上万的图标上传到Iconfinder,同时也伴随而来大量的盗版图。Iconfinder 工程师 Silviu Tantos 在本文中提出一个新颖巧妙的图像查重技术,以杜绝盗版。
我们将在未来几周之内推出一个检测上传图标是否重复的功能。例如,如果用户下载了一个图标然后又试图通过上传它来获利(曾发生过类似案例),那么通过我们的方法,就可以检测出该图标是否已存在,并且标记该账户欺诈。在大量文件中检测某文件是否已经存在的一个常用方法是,通过计算数据集中每一个文件的哈希值,并将该哈希值存储在数组库中。当想要查找某特定文件时,首先计算该文件哈希值,然后在数据库中查找该哈希值。
选择一个哈希算法
加密哈希算法是一个常用的哈希算法。类似MD5,SHA1,SHA256这种在任何一种语言都可以找到可调用的标准库,它们对于简单的用例非常有效。
例如,在Python中先导入hashlib模块,然后调用函数就可以生成某一个字符串或者文件的哈希值。
|
1
2
3
4
5
6
7
8
9
10
|
>>> import hashlib# Calculating the hash value of a string.>>> hashlib.md5('The quick brown fox jumps over the lazy dog').hexdigest()'9e107d9d372bb6826bd81d3542a419d6'# Loading an image file into memory and calculating it's hash value.>>> image_file = open('data/cat_grumpy_orig.png').read()>>> hashlib.md5(image_file).hexdigest()'3e1f6e9f2689d59b9ed28bcdab73455f' |
这个算法对于未被篡改的上传文件非常有效,如果输入数据有细微变化,加密哈希算法都会导致雪崩效应,从而造成新文件的哈希值完全不同于原始文件哈希值。
比如下面这个例子,它在句子的结尾多加了一个句号。
|
1
2
3
4
5
6
7
|
# Original text.>>> hashlib.md5('The quick brown fox jumps over the lazy dog').hexdigest()'9e107d9d372bb6826bd81d3542a419d6'# Slight modification of the text.>>> hashlib.md5('The quick brown fox jumps over the lazy dog.').hexdigest()'e4d909c290d0fb1ca068ffaddf22cbd0' |
例如,修改图像中猫咪鼻子的颜色后,图像的哈希值将改变。
![]()
Original image Modified image
|
1
2
3
4
5
6
7
8
9
|
# Load the original image into memory and calculate it's hash value.>>> image_file = open('data/cat_grumpy_orig.png').read()>>> hashlib.md5(image_file).hexdigest()'3e1f6e9f2689d59b9ed28bcdab73455f'# Load the modified image into memory and calculate it's hash value.>>> image_file_modified = open('data/cat_grumpy_modif.png').read()>>> hashlib.md5(image_file_modified).hexdigest()'12d1b9409c3e8e0361c24beaee9c0ab1' |
目前已有许多感知哈希算法,本文将要提出一个新的dhash(差异哈希)算法,该算法计算相邻像素之间的亮度差异并确定相对梯度。对于以上的用例,感知哈希算法将非常有效。感知哈希算法从文件内容的各种特征中获得一个能够灵活分辨不同文件微小区别的多媒体文件指纹。
dHash
深入学习dHash算法前,先介绍一些基础知识。一个彩色图像是由RGB三原色组成,可以看成一个红绿蓝三原色的颜色集。比如利用用Python图像库(PIL)加载一个图像,并打印像素值。
![]()
Test image
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
>>> from PIL import Image>>> test_image = Image.open('data/test_image.jpg')# The image is an RGB image with a size of 8x8 pixels.>>> print 'Image Mode: %s' % test_image.modeImage Mode: RGB>>> print 'Width: %s px, Height: %s px' % (test_image.size[0], test_image.size[1])Width: 4 px, Height: 4 px# Get the pixel values from the image and print them into rows based on# the image's width.>>> width, height = test_image.size>>> pixels = list(test_image.getdata())>>> for col in xrange(width):... print pixels[col:col+width]...[(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 255)][(0, 0, 0), (212, 45, 45), (51, 92, 154), (130, 183, 47)][(206, 210, 198), (131, 78, 8), (131, 156, 180), (117, 155, 201)][(104, 133, 170), (215, 130, 20), (153, 155, 155), (104, 142, 191)] |
现在我们回到dHash算法,该算法有四个步骤,本文详细说明每一步并验证它在原始图像和修改后图像的效果。前三个像素的红绿蓝颜色强度值分别为255,其余两个颜色强度值分别为0,纯黑色像素三原色为0,纯白色像素三原色为255。其它颜色像素则是由不同强度三原色值组成的。
1.图像灰度化
通过灰度化图像,将像素值减少到一个发光强度值。例如,白色像素(255、255、255)成为255而黑色像素(0,0,0)强度值将成为0。
![]()
Original image (after step 1) Modified image (after step 1)
2.将图像缩小到一个常见大小
将图像缩减到一个常见基础尺寸,比如宽度大高度一个像素值的9*8像素大小(到第三步你就能明白为什么是这个尺寸)。通过这个方法将图像中的高频和细节部分移除,从而获得一个有72个强度值的样本。由于调整或者拉伸图像并不会改变它的哈希值,所以将所有图像归一化到该大小。

Original image (after step 2) Modified image (after step 2)
3.比较邻域像素
前两步实现后得到一个强度值列表,比较该二进制值数组的每一行的相邻像素。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
>>> from PIL import Image>>> img = Image.open('data/cat_grumpy_orig_after_step_2.png')>>> width, height = img.size>>> pixels = list(img.getdata())>>> for col in xrange(width):... print pixels[col:col+width]...[254, 254, 255, 253, 248, 254, 255, 254, 255][254, 255, 253, 248, 254, 255, 254, 255, 255][253, 248, 254, 255, 254, 255, 255, 255, 222][248, 254, 255, 254, 255, 255, 255, 222, 184][254, 255, 254, 255, 255, 255, 222, 184, 177][255, 254, 255, 255, 255, 222, 184, 177, 184][254, 255, 255, 255, 222, 184, 177, 184, 225][255, 255, 255, 222, 184, 177, 184, 225, 255] |
第一个值254和第二个254做比较,第二个值和第三个值比,以此类推,从而每行得到8个布尔值。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
>>> difference = []>>> for row in xrange(height):... for col in xrange(width):... if col != width:... difference.append(pixels[col+row] > pixels[(col+row)+1])...>>> for col in xrange(width-1):... print difference[col:col+(width-1)]...[False, False, True, True, False, False, True, False][False, True, True, False, False, True, False, False][True, True, False, False, True, False, False, False][True, False, False, True, False, False, False, True][False, False, True, False, False, False, True, True][False, True, False, False, False, True, True, False][True, False, False, False, True, True, False, False][False, False, False, True, True, False, False, True] |
4.转换为二值
为了方便哈希值存储和使用,将8个布尔值转换为16进制字符串。Ture变成1,而False变成0。
Python实现
下面是完整Python实现的完成算法:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
def dhash(image, hash_size = 8): # Grayscale and shrink the image in one step. image = image.convert('L').resize( (hash_size + 1, hash_size), Image.ANTIALIAS, ) pixels = list(image.getdata()) # Compare adjacent pixels. difference = [] for row in xrange(hash_size): for col in xrange(hash_size): pixel_left = image.getpixel((col, row)) pixel_right = image.getpixel((col + 1, row)) difference.append(pixel_left > pixel_right) # Convert the binary array to a hexadecimal string. decimal_value = 0 hex_string = [] for index, value in enumerate(difference): if value: decimal_value += 2**(index % 8) if (index % 8) == 7: hex_string.append(hex(decimal_value)[2:].rjust(2, '0')) decimal_value = 0 return ''.join(hex_string) |
最常见情况,图片稍有不同,哈希值很可能是相同的,所以我们可以直接比较。
|
1
2
3
4
5
6
7
8
9
10
|
>>> from PIL import Image>>> from utility import dhash, hamming_distance>>> orig = Image.open('data/cat_grumpy_orig.png')>>> modif = Image.open('data/cat_grumpy_modif.png')>>> dhash(orig)'4c8e3366c275650f'>>> dhash(modif)'4c8e3366c275650f'>>> dhash(orig) == dhash(modif)True |
如果有一个保存哈希值的SQL数据库, 可以这样简单判断哈希值“4 c8e3366c275650f ”是否存在:
|
1
2
|
SELECT pk, hash, file_path FROM image_hashes WHERE hash = '4c8e3366c275650f'; |
现在,对于一些有较大差别的图像,它们的哈希值可能是不相同的,那么需要计算由一个字符串变成另一个字符串所需替换的最少字符数,即汉明距离。
维基百科上有一些计算两个字符串之间的汉明距离的Python示例代码。但是也可以直接基于MySQL数据库上的计算和查询来实现。
|
1
2
3
4
5
6
|
SELECT pk, hash, BIT_COUNT( CONV(hash, 16, 10) ^ CONV('4c8e3366c275650f', 16, 10)) as hamming_distance FROM image_hashes HAVING hamming_distance < 4 ORDER BY hamming_distance ASC; |
对所查询的值与数据库中的哈希值进行异或操作,计数不同位数。由于BIT_COUNT只能操作整数,所以要将所有十六进制的哈希值转成十进制。
结束语
本文使用Python实现了所介绍的算法,当然了读者可以使用任何编程语言实现算法。
在简介中提过,本文算法将应用到Iconfinder上去防止重复提交图标,可以预想,感知哈希算法还有更多实际应用。因为有相似特征的图像的哈希值也是相似的,所以它可以帮助图像推荐系统寻找相似图像。
Iconfinder 如何杜绝盗版,哈希算法检测图像重复的更多相关文章
- 转:MD5(Message-Digest Algorithm 一种哈希算法)
什么是MD5算法 MD5讯息摘要演算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码杂凑函数,可以产生出一个128位元(16位元组)的散列值(hash val ...
- java单向加密算法小结(2)--MD5哈希算法
上一篇文章整理了Base64算法的相关知识,严格来说,Base64只能算是一种编码方式而非加密算法,这一篇要说的MD5,其实也不算是加密算法,而是一种哈希算法,即将目标文本转化为固定长度,不可逆的字符 ...
- [基础技能] 安全技术——哈希算法密码破解之彩虹表(Rainbow Table)学习
1.基础知识 刚刚学习过数字签名的相关知识,以及数字签名的伪造技术,而伪造数字签名归根结底就是密码破解的一个过程,然而直接破解的速度是非常缓慢的,所以有人想出一种办法,直接建立出一个数据文件,里面事先 ...
- Kosaraju 算法检测有向图的强连通性
给定一个有向图 G = (V, E) ,对于任意一对顶点 u 和 v,有 u --> v 和 v --> u,亦即,顶点 u 和 v 是互相可达的,则说明该图 G 是强连通的(Strong ...
- .NET平台开源项目速览(12)哈希算法集合类库HashLib
.NET的System.Security.Cryptography命名空间本身是提供加密服务,散列函数,对称与非对称加密算法等功能.实际上,大部分情况下已经满足了需求,而且.NET实现的都是目前国际上 ...
- 一致性哈希算法与Java实现
原文:http://blog.csdn.net/wuhuan_wp/article/details/7010071 一致性哈希算法是分布式系统中常用的算法.比如,一个分布式的存储系统,要将数据存储到具 ...
- 五分钟理解一致性哈希算法(consistent hashing)
转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT)实现算法 ...
- 每天进步一点点——五分钟理解一致性哈希算法(consistent hashing)
转载请说明出处:http://blog.csdn.net/cywosp/article/details/23397179 一致性哈希算法在1997年由麻省理工学院提出的一种分布式哈希(DHT) ...
- C# MD5摘要算法、哈希算法
MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致.是计算机广泛使用的杂凑算法之一(又译摘要算法.哈希算法) MD5算法具有以下特点: 1.压缩 ...
随机推荐
- 在Web Api中快速实现JSonp
本文翻译自:http://www.codeproject.com/Tips/631685/JSONP-in-ASP-NET-Web-API-Quick-Get-Started Concept: 同源策 ...
- Spring Tool Suite(简称STS)针对SimpleDateFormat.pase函数的实参值不做检验,异常直接默认值之
Spring Tool Suite(简称STS)是 Spring 团队开发的一款基于Eclipse的IDE,旨在简化开发Spring MVC 应用的流程.可以自动生成spring相关的配置文件.比如a ...
- nest expression & Pyparsing
http://pyparsing.wikispaces.com/ http://bbs.csdn.net/topics/330052586 C++ boost "<([^<> ...
- 怎样在ios开发中设置tableview的cell颜色
//方法一: cell .contentView .backgroundColor = [ UIColor redColor ]; //方法二: UITableViewCell *cell = [ta ...
- CLR和.Net对象
CLR和.Net对象生存周期 前言 1. 基础概念明晰* 1.1 公告语言运行时* 1.2 托管模块* 1.3 对象和类型* 1.4 垃圾回收器 2. 垃圾回收模型* 2.1 为什么需要垃圾回收* 2 ...
- Spring中的p标签(转)good
Spring的p标签是基于XML Schema的配置方式,目的是为了简化配置方式. 在XML文件头部添加xmlns:p="http://www.springframework.org/sch ...
- js实现表格的选中一行-------Day58
最開始想很多其它的用js来动态操作表格,是由于在应用了easyUI之后,发现直接写一个<table id="tt"></table>,这就够了,界面里面就剩 ...
- 【翻译】Sencha Ext JS 5公布
原文:Announcing Sencha Ext JS 5 简单介绍 我代表Sencha和整个Ext JS团队,非常自豪的宣布,在今天,Sencha Ext JS 5公布了.Ext JS 5已经迈出了 ...
- twitter分享问题(四)—— Unknown error(api v1过度到V1.1产生)
unknow error! 今天为了使用GA(谷歌分析)追踪twitter分享,又测试了一下twitter分享功能,发现又出问题(使用sharekit分享).就是“unknow error”,之前也碰 ...
- EL表达式(3)
本篇讲解使用EL表达式来调用Java方法(自定义EL函数)和Sun公司开发的EL函数库. 简单来说,我们在一个类中的某个方法,可以使用EL进行调用,这个能被EL表达式调用的方法称之为EL函数,但是这种 ...