筛选目录下所有图片,是否有相同的图片存在,存在的话打印出相同的两张图的具体路径。
成品界面如下(MAC和windows版本界面是一样):
首先是先拿到路径下所有的图片,对比图片的相似度,经过百度有可以直接引用一个python的hash算法进行比较,传入图片路径和设定误差值即可。将相同的图片信息存在txt文件中用来保存。
考虑到路径下可能存在多个文件夹嵌套、图片名字相同的情况,传入hash算法的文件名必须是带路径的,否则只能检测当前目录下的图片,并不能遍历到子文件夹中。
大概思路如下:
1. 获取所有文件名(带路径)
com_path = [""]
def get_all(path):
paths = os.listdir(path) # 列出指定路径下的所有目录和文件
for i in paths:
com_path = os.path.join(path,i)
if os.path.isdir(com_path):
get_all(com_path) # 如果该路径是目录,则调用自身方法
elif os.path.isfile(com_path):
if com_path.split(".")[-1] in ['jpg','png','bmp']:
com_path_T = {com_path.split('\\')[-1]:com_path}
compath.update(com_path_T)
用递归遍历获取所有带路径的文件名,将文件名和带路径的文件名存入字典中。开始考虑用列表存,但是在通过append函数循环添加时,参数是list时报错,所有就用的字典存放,后面查询也方便。
2. 通过字符串切片用"."进行分割出后缀,即文件格式得到所有的图片
3. 拿到所有图片并带路径,循环两两比较图片,调用比较函数
hash算法比较图片函数(传入图片路径可直接使用):
def compare_image_with_hash(image_file_name_1, image_file_name_2, max_dif=0):
ImageFile.LOAD_TRUNCATED_IMAGES = True
hash_1 = None
hash_2 = None
image_file1 = open(image_file_name_1,'rb').read()
Has = hashlib.md5(image_file1).hexdigest()
image_file2 = open(image_file_name_2,'rb').read()
Has1 = hashlib.md5(image_file2).hexdigest()
if image_file1 != image_file2:
return False
with open(image_file_name_1, 'rb') as fp:
hash_1 = imagehash.average_hash(Image.open(fp))
with open(image_file_name_2, 'rb') as fp:
hash_2 = imagehash.average_hash(Image.open(fp)) #对比图片是否一样
dif = hash_1 - hash_2
if dif < 0:
dif = -dif
if dif <= max_dif:
return True
else:
return False
函数compare_image_with_hash本身比较图片hash值的时候,发现对于一个图只改变了颜色,hash值是不变的,因此需要用image_file2 = open(image_file_name_2,'rb').read()
Has1 = hashlib.md5(image_file2).hexdigest()加以判断。
4. 关于Windows打包,比较简单:
- 使用Python打包EXE可执行文件时,需要使用Pyinstaller,使用命令pip install PyInstaller进行安装
- pyinstaller -F -w Test.py命令进行打包(具体见转载博客:https://blog.csdn.net/weixin_39000819/article/details/80942423)
-c | 显示命令行窗口 | 与-w相反,默认含有此参数 |
-w | 不显示命令行窗口 | 编写GUI程序时使用此参数有用。 |
-F | 生成one-file的程序 | 生成结果是一个exe文件,所有的第三方依赖、资源和代码均被打包进该exe内 |
出现上述问题,一般是因为磁盘不够运行导致的。跟打包的exe没有关系。
5. 关于Mac环境,遇到比较多的问题(打包app暂时没有解决):
首先装py2applet后,输入以下命令即可。打包前需要把第三方库,编辑setup.py手动添加第三方库。
py2applet --make-setup xxx.py
python setup.py py2app
6. 线程简单用法
def thread_it(func, *args):
'''将函数放入线程中执行'''
# 创建线程
t = threading.Thread(target=func, args=args)
# 守护线程
t.setDaemon(True)
# 启动线程
t.start()
例如在按键启动线程执行函数:tkinter.Button(Root,text="开始查找",command=lambda :thread_it(getuser(函数名),函数参数)).pack()
7. tkinter基本用法
Root=tkinter.Tk() #创建Tk对象
Root.title("xxxxx") #设置窗口标题
Root.geometry("300x160+500+200") #设置窗口尺寸
XX=tkinter.Label(Root,text="XXXXXXXX") #标签
XX.pack() #指定包管理器放置组件
该方法没有对位置进行设置,可以用label.place(x=20, y=20)进行位置设置。
--------------------------------------源码----------------------------------------------
#coding=utf-8
from tkinter import *
import tkinter as tk
from tkinter import Canvas as C
import tkinter,time,decimal,math,string
import tkinter.messagebox #这个是消息框,对话框的关键
import tkinter.font as tkFont
import os
from PIL import Image
from PIL import ImageFile
import imagehash
import threading
import hashlib
def thread_it(func, *args):
'''将函数放入线程中执行'''
# 创建线程
t = threading.Thread(target=func, args=args)
# 守护线程
t.setDaemon(True)
# 启动线程
t.start()
def clear():
if tkinter.messagebox.askokcancel('提示', '确认结束吗'):
root.quit()
def compare_image_with_hash(image_file_name_1, image_file_name_2, max_dif=0):
ImageFile.LOAD_TRUNCATED_IMAGES = True
hash_1 = None
hash_2 = None
image_file1 = open(image_file_name_1,'rb').read()
Has = hashlib.md5(image_file1).hexdigest()
image_file2 = open(image_file_name_2,'rb').read()
Has1 = hashlib.md5(image_file2).hexdigest()
if image_file1 != image_file2:
return False
with open(image_file_name_1, 'rb') as fp:
hash_1 = imagehash.average_hash(Image.open(fp))
with open(image_file_name_2, 'rb') as fp:
hash_2 = imagehash.average_hash(Image.open(fp)) #对比图片是否一样
dif = hash_1 - hash_2
if dif < 0:
dif = -dif
if dif <= max_dif:
return True
else:
return False
com_path = [""]
compath = {} #字典
def get_all(path):
#path =r'D:\Test3'
paths = os.listdir(path) # 列出指定路径下的所有目录和文件
for i in paths:
com_path = os.path.join(path,i)
# print(com_path)
if os.path.isdir(com_path):
get_all(com_path) # 如果该路径是目录,则调用自身方法
elif os.path.isfile(com_path):
#print(com_path.split(".")[-1])
if com_path.split(".")[-1] in ['jpg','png','bmp','tif','gif','pcx','tga','exif','fpx','svg','psd','cdr','pcd','dxf','ufo','eps','ai','raw','WMF','webp','avif'
'JPG','PNG','BMP','TIF','GIF','PCX','TGA','EXIF','FPX','SVG','PSD','CDR','PCD','DXF','UFO','EPS','AI','RAW','WMF','WEBP','AVIF'
]:
com_path_T = {com_path.split('\\')[-1]:com_path}
compath.update(com_path_T)
#print(com_path.split('\\')[-1],"======",com_path)
get_all(path=r'.')
keyList = []
valueList = []
print(len(compath))
for key in compath.keys():
keyList.append(key)
for value in compath.values():
valueList.append(value)
def getuser():
line_new = open("test.txt",'w')
FlagNum = 0
strInfoT = "正在循环对比..."
tkinter.Label(root,text=strInfoT).place(x=50, y=70)
for i in range(0,len(valueList),1):
strInfoNum = "已完成"+str(i+1)+"个..."
tkinter.Label(root,text=strInfoNum).place(x=160, y=70)
for j in range(1,len(valueList),1):
if i!=j and i<j:
if compare_image_with_hash(valueList[i],valueList[j],0) == True:
FlagNum = 1
line_new.write(valueList[i]+"和"+valueList[j]+"相同\n")
line_new.close()
if FlagNum == 1:
tkinter.messagebox.showwarning('警告','图片相同,相同照片以保持至根目录下的test.txt中,请确认!!')
elif FlagNum == 0:
tkinter.messagebox.showwarning('提示','当前路径下没有相同的图片!')
else:
tkinter.messagebox.showwarning('异常','异常情况!')
root = tkinter.Tk() # 创建窗口对象的背景色
root.geometry("280x150+600+200")
root.title("图片筛查工具")
strInfo = "需要循环比较 "+str(len(compath))+" 个文件,请稍等!"
tkinter.Label(root,text=strInfo).place(x=50, y=40)
tkinter.Button(root,text="开始筛查",command=lambda :thread_it(getuser)).place(x=50, y=100) #command绑定获取文本框内容方法
tkinter.Button(root,text="结束筛查",command= clear).place(x=170, y=100) #command绑定获取文本框内容方法
root.mainloop() #进入主循环