图片重复检测与删除工具
本文最后更新于 2025-12-27,文章内容可能已经过时。
这是一个基于 Python 开发的图形界面应用程序,用于检测和删除文件夹中的重复图片文件。该工具使用感知哈希算法(Perceptual Hash)来识别视觉上相似或完全相同的图片,并提供一键删除重复项的功能。
功能特点
图形用户界面:基于 tkinter 构建,操作简单直观
智能检测:使用 imagehash 库的感知哈希算法检测重复图片
批量处理:支持递归扫描整个文件夹及其子文件夹
安全删除:每组重复图片中只保留一张,其余删除
格式支持:支持 JPG、JPEG、PNG、GIF、BMP、TIFF、WebP 等常见图片格式
使用方法
运行程序:
python 1.py点击“浏览...”按钮选择包含图片的文件夹
点击“开始检测重复图片”按钮开始扫描
程序会显示检测到的重复图片组
确认后选择是否删除重复项
工作原理
程序遍历选定文件夹中的所有图片文件
为每张图片计算感知哈希值
将具有相同哈希值的图片归为一组(即重复图片)
每组中保留第一张图片,删除其余重复项
依赖库
Python 3.x
Pillow (PIL)
imagehash
tkinter (通常随 Python 一起安装)
安装依赖
pip install Pillow imagehash注意事项
删除操作不可逆,请在重要文件夹使用前备份
程序根据感知哈希算法判断图片相似性,极少数情况下可能误判
仅保留每组重复图片中的第一张,如需保留特定图片,请在处理前重命名
许可证
本项目为开源软件,遵循 MIT 许可证。
代码
GitHub链接:点我
import os
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from PIL import Image
import imagehash
class ImageDuplicateRemover:
def __init__(self, root):
self.root = root
self.root.title("图片重复检测与删除工具")
self.root.geometry("700x500")
self.root.resizable(True, True)
# 设置界面样式
self.style = ttk.Style()
self.style.configure("TButton", font=("微软雅黑", 10))
self.style.configure("TLabel", font=("微软雅黑", 10))
self.style.configure("Accent.TButton", font=("微软雅黑", 10, "bold"))
# 文件夹路径变量
self.folder_path = tk.StringVar()
# 创建界面元素
self.create_widgets()
def create_widgets(self):
# 顶部框架:选择文件夹
top_frame = ttk.Frame(self.root, padding="10")
top_frame.pack(fill=tk.X)
ttk.Label(top_frame, text="选择图片文件夹:").pack(side=tk.LEFT, padx=5)
ttk.Entry(top_frame, textvariable=self.folder_path, width=50).pack(side=tk.LEFT, padx=5, fill=tk.X, expand=True)
ttk.Button(top_frame, text="浏览...", command=self.browse_folder).pack(side=tk.LEFT, padx=5)
# 中间按钮:开始检测
mid_frame = ttk.Frame(self.root, padding="10")
mid_frame.pack()
ttk.Button(mid_frame, text="开始检测重复图片", command=self.start_detection, style="Accent.TButton").pack()
# 底部框架:结果显示
bottom_frame = ttk.Frame(self.root, padding="10")
bottom_frame.pack(fill=tk.BOTH, expand=True)
ttk.Label(bottom_frame, text="处理结果:").pack(anchor=tk.W)
# 滚动条和文本框
scrollbar = ttk.Scrollbar(bottom_frame)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.result_text = tk.Text(bottom_frame, wrap=tk.WORD, yscrollcommand=scrollbar.set, height=15)
self.result_text.pack(fill=tk.BOTH, expand=True)
scrollbar.config(command=self.result_text.yview)
# 禁止编辑文本框
self.result_text.config(state=tk.DISABLED)
def browse_folder(self):
"""打开文件夹选择对话框"""
folder_selected = filedialog.askdirectory()
if folder_selected:
self.folder_path.set(folder_selected)
def log(self, message):
"""在结果文本框中添加日志信息"""
self.result_text.config(state=tk.NORMAL)
self.result_text.insert(tk.END, message + "\n")
self.result_text.see(tk.END) # 滚动到最后一行
self.result_text.config(state=tk.DISABLED)
self.root.update_idletasks() # 更新界面
def get_image_hash(self, image_path):
"""计算图片的感知哈希值"""
try:
with Image.open(image_path) as img:
# 转换为灰度图并计算哈希值
return imagehash.phash(img)
except Exception as e:
self.log(f"无法处理图片 {os.path.basename(image_path)}: {str(e)}")
return None
def is_image_file(self, filename):
"""判断文件是否为图片"""
image_extensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.webp']
ext = os.path.splitext(filename)[1].lower()
return ext in image_extensions
def start_detection(self):
"""开始检测并删除重复图片"""
folder = self.folder_path.get()
if not folder or not os.path.isdir(folder):
messagebox.showerror("错误", "请选择有效的文件夹")
return
# 清空之前的结果
self.result_text.config(state=tk.NORMAL)
self.result_text.delete(1.0, tk.END)
self.result_text.config(state=tk.DISABLED)
self.log(f"开始检测文件夹: {folder}")
# 获取所有图片文件
image_files = []
for root_dir, _, files in os.walk(folder):
for file in files:
if self.is_image_file(file):
image_files.append(os.path.join(root_dir, file))
self.log(f"找到 {len(image_files)} 个图片文件")
if len(image_files) == 0:
self.log("没有找到图片文件")
return
# 计算哈希值并分组重复图片
hash_dict = {}
for img_path in image_files:
img_hash = self.get_image_hash(img_path)
if img_hash:
if img_hash not in hash_dict:
hash_dict[img_hash] = []
hash_dict[img_hash].append(img_path)
# 处理重复图片
duplicate_groups = [group for group in hash_dict.values() if len(group) > 1]
self.log(f"发现 {len(duplicate_groups)} 组重复图片")
if not duplicate_groups:
self.log("没有发现重复图片")
return
# 询问用户是否删除重复图片
response = messagebox.askyesno("确认删除",
f"发现 {len(duplicate_groups)} 组重复图片,是否删除重复项?\n只保留每组中的一张图片")
if not response:
self.log("用户取消了删除操作")
return
# 删除重复图片
deleted_count = 0
for group in duplicate_groups:
# 保留第一张,删除其余的
keep_file = group[0]
self.log(f"\n保留图片: {os.path.basename(keep_file)}")
for file_to_delete in group[1:]:
try:
os.remove(file_to_delete)
self.log(f"已删除重复图片: {os.path.basename(file_to_delete)}")
deleted_count += 1
except Exception as e:
self.log(f"删除图片 {os.path.basename(file_to_delete)} 失败: {str(e)}")
self.log(f"\n操作完成,共删除 {deleted_count} 张重复图片")
messagebox.showinfo("完成", f"操作完成,共删除 {deleted_count} 张重复图片")
if __name__ == "__main__":
root = tk.Tk()
app = ImageDuplicateRemover(root)
root.mainloop()
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 研云代码
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果