1. # -*- coding: utf-8 -*-
  2. '''
  3. Python用哈希算法查找相似图片并放入[_df]的文件夹中
  4.  
  5. 相似图片包括不同分辨率,不同大小,不同格式,只要图片相似就会算重复文件
  6.  
  7. 安装cv2
  8. pip install opencv-python
  9.  
  10. '''
  11. import os
  12. import cv2
  13. import numpy as np
  14. import shutil
  15. import random
  16.  
  17. class DuplicateFiles (object):
  18. dir = ''
  19. def __init__(self, dir):
  20. self.dir = dir # 实例属性
  21.  
  22. # 均值哈希算法
  23. def aHash(self,img,shape=(10,10)):
  24. # 缩放为10*10
  25. img = cv2.resize(img, shape)
  26. # 转换为灰度图
  27. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  28. # s为像素和初值为0,hash_str为hash值初值为''
  29. s = 0
  30. hash_str = ''
  31. # 遍历累加求像素和
  32. for i in range(shape[0]):
  33. for j in range(shape[1]):
  34. s = s + gray[i, j]
  35. # 求平均灰度
  36. avg = s / 100
  37. # 灰度大于平均值为1相反为0生成图片的hash值
  38. for i in range(shape[0]):
  39. for j in range(shape[1]):
  40. if gray[i, j] > avg:
  41. hash_str = hash_str + '1'
  42. else:
  43. hash_str = hash_str + '0'
  44. return hash_str
  45.  
  46. # 差值感知算法
  47. def dHash(self,img,shape=(10,10)):
  48. # 缩放10*11
  49. img = cv2.resize(img, (shape[0]+1, shape[1]))
  50. # 转换灰度图
  51. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  52. hash_str = ''
  53. # 每行前一个像素大于后一个像素为1,相反为0,生成哈希
  54. for i in range(shape[0]):
  55. for j in range(shape[1]):
  56. if gray[i, j] > gray[i, j + 1]:
  57. hash_str = hash_str + '1'
  58. else:
  59. hash_str = hash_str + '0'
  60. return hash_str
  61.  
  62. # 感知哈希算法(pHash)
  63. def pHash(self,img,shape=(10,10)):
  64. # 缩放32*32
  65. img = cv2.resize(img, (32, 32)) # , interpolation=cv2.INTER_CUBIC
  66.  
  67. # 转换为灰度图
  68. gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
  69. # 将灰度图转为浮点型,再进行dct变换
  70. dct = cv2.dct(np.float32(gray))
  71. # opencv实现的掩码操作
  72. dct_roi = dct[0:10, 0:10]
  73.  
  74. hash = []
  75. avreage = np.mean(dct_roi)
  76. for i in range(dct_roi.shape[0]):
  77. for j in range(dct_roi.shape[1]):
  78. if dct_roi[i, j] > avreage:
  79. hash.append(1)
  80. else:
  81. hash.append(0)
  82. return hash
  83.  
  84. # 通过得到RGB每个通道的直方图来计算相似度
  85. def classify_hist_with_split(self,image1, image2, size=(256, 256)):
  86. # 将图像resize后,分离为RGB三个通道,再计算每个通道的相似值
  87. image1 = cv2.resize(image1, size)
  88. image2 = cv2.resize(image2, size)
  89. sub_image1 = cv2.split(image1)
  90. sub_image2 = cv2.split(image2)
  91. sub_data = 0
  92. for im1, im2 in zip(sub_image1, sub_image2):
  93. sub_data += self.calculate(im1, im2)
  94. sub_data = sub_data / 3
  95. return sub_data
  96.  
  97. # 计算单通道的直方图的相似值
  98. def calculate(self,image1, image2):
  99. hist1 = cv2.calcHist([image1], [0], None, [256], [0.0, 255.0])
  100. hist2 = cv2.calcHist([image2], [0], None, [256], [0.0, 255.0])
  101. # 计算直方图的重合度
  102. degree = 0
  103. for i in range(len(hist1)):
  104. if hist1[i] != hist2[i]:
  105. degree = degree + (1 - abs(hist1[i] - hist2[i]) / max(hist1[i], hist2[i]))
  106. else:
  107. degree = degree + 1
  108. degree = degree / len(hist1)
  109. return degree
  110.  
  111. # Hash值对比
  112. def cmpHash(self,hash1, hash2,shape=(10,10)):
  113. n = 0
  114. # hash长度不同则返回-1代表传参出错
  115. if len(hash1)!=len(hash2):
  116. return -1
  117. # 遍历判断
  118. for i in range(len(hash1)):
  119. # 相等则n计数+1,n最终为相似度
  120. if hash1[i] == hash2[i]:
  121. n = n + 1
  122. return n/(shape[0]*shape[1])
  123.  
  124. def mymovefile(self,srcfile,dstpath,ffname): # 移动函数
  125. if not os.path.isfile(srcfile):
  126. print ("%s not exist!"%(srcfile))
  127. else:
  128. fpath,fname=os.path.split(srcfile) # 分离文件名和路径
  129. if(ffname):fname=ffname
  130. if not os.path.exists(dstpath):
  131. os.makedirs(dstpath) # 创建路径
  132. shutil.move(srcfile, dstpath + fname) # 移动文件
  133. #print ("move %s -> %s"%(srcfile, dstpath + fname))
  134.  
  135. # 定义函数
  136. def list_all_files(self,rootdir):
  137. _files = []
  138. # 列出文件夹下所有的目录与文件
  139. list = os.listdir(rootdir)
  140. for i in range(0, len(list)):
  141. # 构造路径
  142. path = os.path.join(rootdir, list[i])
  143. # 判断路径是否为文件目录或者文件
  144. # 如果是目录则继续递归
  145. if os.path.isdir(path):
  146. _files.extend(list_all_files(path))
  147. if os.path.isfile(path):
  148. _files.append(path)
  149. return _files
  150.  
  151. #处理文件
  152. def mvPhoto(self):
  153.  
  154. photoList = self.list_all_files(self.dir)
  155. #print(photoList)
  156.  
  157. for i,photo in enumerate(photoList):
  158. mvPhoto = False #是否移动主文件
  159. #如果不是文件则跳出
  160. if(not os.path.isfile(photo)):
  161. continue
  162. fpath,fname=os.path.split(photo)
  163. print('Master:'+fname)
  164. ffname = fname.split('.')
  165.  
  166. #不是下列文件形式跳出
  167. if(ffname[1] not in {'jpg', 'bmp', 'png', 'jpeg', 'gif'}):
  168. continue
  169.  
  170. img1 = cv2.imdecode(np.fromfile(photo,dtype=np.uint8),cv2.IMREAD_COLOR)
  171. for j in range(i+1,len(photoList)):
  172. #print(' ',j,photoList[j])
  173. if(not os.path.isfile(photo) or not os.path.isfile(photoList[j])):
  174. continue
  175. spath,sname=os.path.split(photoList[j])
  176. #print(sname)
  177. ssname = sname.split('.')
  178. if(ssname[1] not in {'jpg', 'bmp', 'png', 'jpeg', 'jfif'}):
  179. continue
  180.  
  181. #img1 = cv2.imread(photo)
  182. img2 = cv2.imdecode(np.fromfile(photoList[j],dtype=np.uint8),cv2.IMREAD_COLOR)
  183.  
  184. #hash1 = aHash(img1)
  185. #hash2 = aHash(img2)
  186. n1 = self.cmpHash(self.aHash(img1), self.aHash(img2))
  187. n2 = self.cmpHash(self.dHash(img1), self.dHash(img2))
  188. n3 = self.cmpHash(self.pHash(img1), self.pHash(img2))
  189. n4 = self.classify_hist_with_split(img1, img2)
  190. n5 = self.calculate(img1, img2)
  191. #print(' ',n1,n2,n3,n4,n5)
  192. if(n1>0.90 or n2>0.90 or n3>0.90 or n4>0.90 or n5>0.90):
  193. mvPhoto = True
  194. print(' move file:'+photoList[j])
  195. if(os.path.isfile(photoList[j])):
  196. print('ffname[0]:'+ffname[0])
  197. #mymovefile(photoList[j],dir+'_重复'+'/',ffname[0]+'_'+str(random.randint(10,99))+'.'+ffname[1])
  198. self.mymovefile(photoList[j],dir+'_df'+'/',ffname[0]+'_'+sname)
  199.  
  200. #最后移动主文件
  201. if(mvPhoto==True):
  202. self.mymovefile(photo,dir+'_df'+'/',fname)
  203.  
  204. if __name__ == "__main__":
  205. #指定路径
  206. #dir = r'E:\python\photoCompare\328' #指定目录地址
  207. dir = os.getcwd() #当前文件所在目录
  208. duplicateFiles = DuplicateFiles(dir)
  209. duplicateFiles.mvPhoto()

  

Python用哈希算法查找相似图片(包括不同分辨率,不同大小,不同格式的图片)的更多相关文章

  1. 将jpg压缩成webp格式的图片

    cwebp名称 cwebp -压缩图像文件为的WebP文件概要 cwebp [选项] INPUT_FILE -o output_file.webp描述 cwebp压缩使用的WebP格式的图像.输入格式 ...

  2. iPhone照片格式heic图片怎么打开

    苹果自iOS11系统之后默认的是heic图片格式,在电脑和安卓中都无法直接查看,需要将其转换图片格式,那苹果heic图片怎么转换成jpg格式?下面我们一起来看看吧! 使用工具:电脑.图片 操作方法: ...

  3. 感知哈希算法——Python实现【转】

    转自:https://blog.csdn.net/m_buddy/article/details/78887248 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原 ...

  4. Iconfinder 如何杜绝盗版,哈希算法检测图像重复

    原地址:http://blog.jobbole.com/65914/ 本文由 伯乐在线 - 小鱼 翻译自 Silviu Tantos.欢迎加入技术翻译小组.转载请参见文章末尾处的要求. [伯乐在线导读 ...

  5. Atitit.java图片图像处理attilax总结  BufferedImage extends java.awt.Image获取图像像素点image.getRGB(i, lineIndex); 图片剪辑/AtiPlatf_cms/src/com/attilax/img/imgx.javacutImage图片处理titit 判断判断一张图片是否包含另一张小图片 atitit 图片去噪算法的原理与

    Atitit.java图片图像处理attilax总结 BufferedImage extends java.awt.Image 获取图像像素点 image.getRGB(i, lineIndex); ...

  6. Notes:一致性哈希算法

    业务场景: 存在三个专门提供缓存服务的服务器,前端所需要的图片等静态资源被缓存于这三个服务器其中之一. 但是如何提高查找图片的速度呢? 可以采用哈希算法. 常规意义上的哈希算法: 通过hash(图片名 ...

  7. os常用模块,json,pickle,shelve模块,正则表达式(实现运算符分离),logging模块,配置模块,路径叠加,哈希算法

    一.os常用模块 显示当前工作目录 print(os.getcwd()) 返回上一层目录 os.chdir("..") 创建文件包 os.makedirs('python2/bin ...

  8. 转 白话解析:一致性哈希算法 consistent hashing

    摘要: 本文首先以一个经典的分布式缓存的应用场景为铺垫,在了解了这个应用场景之后,生动而又不失风趣地介绍了一致性哈希算法,同时也明确给出了一致性哈希算法的优点.存在的问题及其解决办法. 声明与致谢: ...

  9. Python多线程问题的资料查找与汇总

    Python多线程问题的资料查找与汇总 声明: 1)本报告由博客园bitpeach撰写,版权所有,免费转载,请注明出处,并请勿作商业用途. 2)若本文档内有侵权文字或图片等内容,请联系作者bitpea ...

  10. ELFhash - 优秀的字符串哈希算法

    ELFhash - 优秀的字符串哈希算法 2016年10月29日 22:12:37 阅读数:6440更多 个人分类: 算法杂论算法精讲数据结构 所属专栏: 算法与数据结构   版权声明:本文为博主原创 ...

随机推荐

  1. scrcpy

    捕获配置 缩小尺寸 有时,以较低的清晰度镜像 Android 设备以提高性能很有用. 将宽度和高度限制为某个值(例如 1024): scrcpy --max-size 1024 scrcpy -m 1 ...

  2. [Cisco] IOS NAT Load-Balancing for Two ISP Connections

    interface FastEthernet0 ip address dhcp ip nat outside ip virtual-reassembly ! interface FastEtherne ...

  3. 一分钟教你分清各种光纤跳线接头(SC、ST、FC、LC、MPO)

    一分钟教你分清各种光纤跳线接头(SC.ST.FC.LC.MPO)  市场上常见的光纤跳线有以下几种接头:SC.ST.FC.LC.MPO,相信很多入门者和小编一样,面对各种英文缩写也是我只认识他们,却不 ...

  4. ValueError: unsupported format character ‘Y‘ (0x59) at index 70

    错误信息:ValueError: unsupported format character 'Y' (0x59) at index 70产生原因:因为python执行的sql中存在类似DATE_FOR ...

  5. tableau连接mysql

    1.下载驱动地址:https://dev.mysql.com/downloads/connector/odbc/ 2.选择MSI Installer自动安装自动配置 3.本地127.0.0.1(其他I ...

  6. element表格样式修改

    HTML代码: <el-table :data="tableData" style="width: 100%" border :row-class-nam ...

  7. django 批量提交

    https://www.cnblogs.com/lbzbky/articles/11792545.html

  8. OSPF与ISIS比较

  9. 宏任务&微处理

    事件循环 JavaScript 语言的一大特点就是单线程,同一个时间只能做一件事.为了协调事件.用户交互.脚本.UI 渲染和网络处理等行为,防止主线程的不阻塞,Event Loop 的方案应用而生. ...

  10. 声网 Agora 音频互动 MoS 分方法:为音频互动体验进行实时打分

    在业界,实时音视频的 QoE(Quality of Experience) 方法一直都是个重要的话题,每年 RTE 实时互联网大会都会有议题涉及.之所以这么重要,其实是因为目前 RTE 行业中还没有一 ...