目前有近 100w 图片需要判重,挑了几个 hash 算法,正在跑 hamming code,都是 128bit 的 binary
这些图片都是经过 md5 与判重之后的图片了,所以需要找出来一些汉明距离接近的肉眼观察一下
所以要找到一些距离是 0,1,2,3 的图片组。
当然了,挨个计算一次(1M * 1M = 1T)
好像似乎,也不是很长时间吧,还勉强能接受,跑几天跑完了
有什么能再快一些的算法吗?
目前有一台机器(9400f+1660s)可以跑一点机器学习,勉强够看。
1
douglas1997 2020-04-25 10:02:11 +08:00
先算出图像指纹,在进行比对?我觉得算法慢是因为直接算一对一对图像的汉明距离,这个才是费时间的,而不是 1M*1M 。
比较简单直接的策略,可以考虑: - 图像降采样 - 取 128Bit 的其中 8Bit/16Bit 作为比对的样本数据 - 生成图像指纹 - 多进程 |
2
mcfog 2020-04-25 10:05:00 +08:00 via Android
简单的削减一下数量级吧,比如按 bit1 的位数分组,只在邻接的 k 组之间比较。或者先按前 k 位分组在组内找,然后按后 k 位分组在组内找
|
3
newdongyuwei 2020-04-25 10:09:04 +08:00 via Android
图片的话 phash,dhash 比较靠谱吧
|
4
momocraft 2020-04-25 10:11:00 +08:00
排序後逐個比對可以嗎? (你的問題好像可以接受實際接近但漏掉的, 即 true negative)
|
5
ipwx 2020-04-25 10:13:48 +08:00
你需要的是,能够用向量空间的欧几里得距离表示图片相似度的某个模型。
因为欧几里得距离下,可以用 KD-tree 进行相似向量查找。整个数据集两两找一遍是 N log N 的。 |
6
momocraft 2020-04-25 10:14:38 +08:00
可能會漏掉非常多, 排序會導致實際只比較 LSB 附近的 bit
|
7
ifzzzh 2020-04-25 10:18:22 +08:00
locality-sensitive hashing 了解一下
|
8
also24 2020-04-25 10:24:19 +08:00 via Android
之前做过类似需求… 也是困扰在这个地方
|
9
liukangxu 2020-04-25 10:29:36 +08:00 1
看看 Google 是怎么做的: https://www2007.org/papers/paper215.pdf
|
10
phpfpm OP @douglas1997 128bit 已经是图像指纹了,不是两个 1M 比指纹,是 1M 个 128bit 的 hash 比距离
|
11
phpfpm OP @newdongyuwei 已经在跑 p,dhash 了
确实 dhash 好一些,还挑了几个别的算法。 全量跑一遍 1M*5 个 hash 算法得两三天(主要是资源在 nas 上 io 慢机器也不快) @momocraft 排序肯定不行,排序相当于只有前缀分组,会漏掉特别多 @ipwx 对,有这玩意么 其实细想一下,汉明距离就是 n-dimission 的距离,不过 nlgn 的算法仅限于 2 维来着? |
13
also24 2020-04-25 10:45:44 +08:00 1
想起来之前做的一个简单优化:
统计 phash 中 1 的个数,然后只搜索对比 1 的数量相近的内容。 假如我们需要汉明距离小于等于 6,那 1 的数量差也不应该大于 6 才对。 |
15
phpfpm OP @all
算了 25k of 720k 的摘要 hash,用 dhash 就能找到约 0.5k 的 dup,相当于汉明距离=0 这个已经足够 make sense 了~ 汉明距离 1+的以后慢慢算,可以结合几种不同的 hash 去重~ |
16
phpfpm OP |
17
ipwx 2020-04-25 10:50:47 +08:00
@phpfpm emmm 其实 kd-tree 是 kN log N,k 是维度。所以 k 不大的情况下,也差不多是 N log N 了。但是它处理的是 特征向量,不是汉明码。汉明码是 0-1 binary 数据,反而不好处理。特征向量是实数向量,反而好处理一些。
产生图片特征向量,其实应该还是个 research topic 。我搞深度学习,但是不搞图片压缩,不太清楚那种算法比较成熟。你要让我想一下哪些方向有可能,然后进一步进入这个领域研究,我可能做得到。但现阶段给不了你意见。 |
18
aec4d 2020-04-25 10:56:15 +08:00 via iPhone 1
ficapy.com/2018/03/04/Postgresql_use_bktree_hamming/ 以前做过类似的,BK 树可以优化
|
19
imn1 2020-04-25 11:27:40 +08:00
如果只是百万级汉明不需要几天啊,我觉得#1 说得对,你是否每次都算一遍?
你应该缓存 hash 值的 然后建议分段,如果有分类别什么的,那将大大降低循环次数 windows 有个软件 Similar Images,32bit,可以缓存 hash,作者应该弃坑了,好几年没更新,百万级也就小时级别(不过一次跑百万可能会崩,只能分段) 我自己写的 python 也不用那么久,只是估计,没试过百万,不过我是 hash 和匹配分开(整理时顺便 hash 入库,目前约 50M 张),然后匹配是家常便饭,经常跑小量(千张到万张)匹配,主要是入库时做去重,已经整理入库的就不需要互相比较了 说说算法,常见的 hash 有这几种 cv2.img_hash_PHash cv2.img_hash_AverageHash cv2.img_hash_RadialVarianceHash cv2.img_hash_MarrHildrethHash cv2.img_hash_ColorMomentHash cv2.img_hash_BlockMeanHash cv2 就是 opencv,BlockMean 最快,但极其不准,基本弃了,RadialVariance/ColorMoment 较慢,但还是有点用,ahash/phash 速度适中,MarrHildrethHash 我很少用,主要是搞不清它什么场合更佳 我个人的经验是,aHahs/pHash 中其中一个匹配( OR ),ColorMomentHash/RadialVarianceHash 中其中一个匹配( OR ),这两组 AND 的话,基本就确定重复了 —— 指没有漏判,然后找出的结果中只有很少是错判,错判不到千分之一(分母是全体,包含不重复的) 不过临界值还是要自己调,aHash/pHash/ColorMomentHash 我都是用 3,RadialVarianceHash 我用 0.95 ,全部都比网上代码样例的建议值宽松很多,单独一个算法用这个临界值的话误判会更大,但我组合起来用误判就很小了,这样比单独一个 hash 使用更严格临界值结果还好 —— 原理是图片质量不高的话,应该宽松些,这样不会漏掉,但很多误判,然后用组合降低误判。如果图片质量很高,例如全部都是没有 PS 的摄影作品( raw 或近似 raw )的话,可以适当调高临界值,甚至只用一种算法也够了 还有一种模式匹配算法,不过只适合小图找大图(就是大图里面的一部分),或者有裁切的图找原图,不适合找相似的图,此处不详说了 |
20
phpfpm OP @imn1 非常感谢这么详细的回答!
其实我还没开始计算百万级的汉明距离(o(n^2)),因为汉明码还没有预处理完——数据在 nas 上得先下载下来再算。 好吧,刚才打点看了一下,不是 io 的问题,就是 hash 算的慢(算 hash 呢,还没比呢) 服务器是一台 thinkpad 的,i5 4200u(1.6G~2.6G) 单线程跑满 hash 一个 100k 的图片大概需要 200ms (单线程),其他开销<5ms(数据库,网络,本地临时文件写) 算法(采用了 ah,dh, PercepiahHash,另外一个包的 ph 和 dh ),都是 php 的,算了 5 个 hash 。 我不太清楚其他语言算这个是不是能快一些,反正服务器也比较渣,之后跑判重肯定换个环境了,只是数据都在一个工程比较好管理。 等 hash 跑完我先用 dist=0 (完全匹配,基于数据库算下增量数据)简单提供一个服务,之后再慢慢跑 dist=1,2,3 的情况。 毛菇如果把历史数据都读取到内存当中算一趟的时间应该在可接受的范围内吧…… |
21
phpfpm OP @imn1 我尝试了一下算 hash dup 的算力。
必要的缓存优化我做了,hash 全部读取到内存没有 io 问题。 计算 5(个算法)*200 个 src*10k 个目标的汉明距离大概需要 1 分钟 i5 [email protected] 睿频到 2.2 的单核 如果目标上升到 1M(100 倍),5*200 这组需要的时间将会上升到 100 分钟 当然换一个好点的 cpu 提升 10 倍也就顶天了,10 分钟算 200 个(因为前面的 target 少) 1M=200*5000, 算均值是 5 分钟一批,需要 25000 分钟,大概 400 个小时。 |
22
phpfpm OP @also24 又做了一个优化,比较 distance 之前算下 bit count 的差值,超过阈值就不算了。
这样又可以快一点点。 |
23
imn1 2020-04-25 19:18:00 +08:00
@phpfpm #21
有点没看明白 你的程序是 compare(pic1, pic2),在里面 hash 然后比较,这样的么? 这样 compare(pic1, pic3)的话,pic1 不是又要重新 hash 一次? 应该转成两个步骤 hash1=img_hash(pic1) hash2=img_hash(pic2) hash3=img_hash(pic3) …… 然后入库 用数据库的值 compare(hash1, hash2),这个不需要多次 hash 了 |
24
phpfpm OP |
25
lizytalk 2020-04-25 19:24:30 +08:00 via iPhone
lsh 可以么?
|
27
imn1 2020-04-25 19:35:15 +08:00
@phpfpm
呃,我想起 RadialVariance/ColorMoment 是用在什么场合较佳了 aHash/pHash/dHash 对于有较大水印的图判断不准,多数为 False,但这两个能判断出是相似的图(True) |
28
also24 2020-04-25 19:37:43 +08:00
@phpfpm #22
对,我说的 1 的数量其实就是 bit count 不过大部分环境下都有对 bit count 方法做了优化的原生方法,刚才忘了说了…… 上次搞这个事儿大概是 4 年前了,所以忘了好多细节 =。= |
30
phpfpm OP @also24 我直接硬数的,反正 n^2 的算法里面的 n 次 bit 计算怎么搞都不差太多……
但是优点确实是能省好多 distance 毛估,distance 计算数量减少百分之 90,但是多算了 n 次绝对值相减,里外里效率提升 50%这样 ext_gmp 的 distance 已经很省时间了 |
32
phpfpm OP |
33
yuruizhe 2020-04-25 19:53:02 +08:00
@phpfpm
有个不成熟的想法,设置一个“绝对距离”的概念 绝对距离=0{0000} 绝对距离=1{0001,0010,0100,1000} 绝对距离=2{0011,0101,0110,...} ... 1.反正每个组内的元素,两两汉明距离都为 2 或 4 或 6 或... 2.在两个临近组间找汉明距离为 1 的两个点 依次类推... (脑袋一拍,没有验证,不要怼我) |
34
JCZ2MkKb5S8ZX9pq 2020-04-25 19:55:30 +08:00
自己本機上處理過,hash 之後丟數據庫了,重複的加數組最後再摘出來。
然後 hash 前分精度,先 8x8 過掉一些,重複的部分再提高精度 hash 。 1Mx1M 這個應該是不必的。 |
35
phpfpm OP |
36
vchar2ex 2020-04-25 20:30:27 +08:00
https://github.com/idealo/imagededup 这里 phash, dhash 有现成的,可以测试一下看看效果如何
|
38
NcAtN 2020-04-25 22:24:06 +08:00
VideoComparerWin 这个是视频查重的..你看下原理 可以用来图片么 还是这个工具直接带图片查重 忘记了
|
39
yuruizhe 2020-04-25 22:38:26 +08:00
@phpfpm
不是真正的分配 2^128 那么多的空间,我的意思是分箱,把哈希码分组存在 subset 里,如果 element 在 subset_1 中,就去 subset_0 与 subset_2 里寻找与 element 的汉明距离为 1 的其他 element |
40
tzm41 2020-04-25 23:57:43 +08:00 via iPhone
我想法感觉跟楼上几位相似。楼主的问题是要求 L1 nearest neighbors,数据是 128-dim binary 。近似加快的方法就是做一个 L2 embedding,感觉应该是 Johnson-Lindenstrauss 之类的。
|
41
0o0o0o0 2020-04-26 07:47:26 +08:00
向量搜索,记得有一个叫 hnsw 的算法。。。不知道可不可以。。。
|
42
phpfpm OP |
43
phpfpm OP 根据上面的帖子优化了一版
从 5*200*25k 个 distance 用 10s 了 到 5*200*200k 个 distance 用 15s 之后一个点的全量数据对比 (5*1M )个 distance 在 20s 内能搞定,考虑用队列离线算~ |