参考资料:http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
很多时候,我们的文件夹里会有重复或者相似的图片,这类图片自然没有必要存在多份,我们要做的就是遍历所有图片,然后找出相似的,只保留其中一张即可。
这里的关键技术叫做”感知哈希算法”(Perceptual hash algorithm),它的作用是对每张图片生成一个”指纹”(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。
另外还找到了其他几种方法,这里是地址
http://www.ruanyifeng.com/blog/2013/03/similar_image_search_part_ii.html
第一步,缩小尺寸。
将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
在macOS,使用的是NSImage
, 两者的一些操作很类似。
1 | - (NSImage *)reSizeImage:(NSImage *)image toSize:(CGSize)reSize |
第二步,简化色彩。
将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
第三步,计算平均值。
计算所有64个像素的灰度平均值。
1 | a[ArrSize] = 0; |
第四步,比较像素的灰度。
将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
1 | a[ArrSize] = 0; |
第五步,计算哈希值。
将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。
得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算”汉明距离”(Hamming distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。
代码实现
上面已经得到了判断两张图片是否相似的方法,而我们要实现的功能是:从一堆图片中找出哪些图片是相似的,并把相似图片放到一起。
实现逻辑:遍历所有图片,第一张图片先放到第一组中,第二张图片先与第一组中的第一张图片做比较,如果相似,就放到第一组中,否则放到第二组中,以此类推,直到遍历完左右图片,这时候,相似的图片都已经分到各个组中,有一些组中可能只有一张图片,表示没有与之相似大的图片,取出图片个数大于1的组,然后挨个显示这个组中的图片,让用户选择哪些图片该保留。
这是一个比较耗时的操作,所以放在子线程中操作。
1 | NSMutableArray *totalArr = [NSMutableArray array]; |
效果图: