Smart Image Organizer Assistant 图片数据集重命名、清理,去重错误文件 Python实现
功能主要围绕处理目录中的图片文件:识别、检查、删除非图片文件以及重命名操作。这个程序是一个很好的例子,展示了如何使用Python的os库和Pillow(PIL的升级版本)库来执行文件系统的操作。
首先,介绍一下Python的os库。这个库提供了一系列的功能,用来与操作系统进行交互,比如文件的创建、删除、遍历目录等操作。而Pillow库是Python中一个非常强大的处理图片的库,它支持打开、修改、保存多种格式的图片文件。
1. 批量获取子文件夹
我们的第一个函数 get_subfolders
的工作是获取给定根目录下的所有子文件夹路径。程序通过列表推导式,结合os.listdir()
和 os.path.join()
方法来完成这项工作。这个函数可以帮助我们完成初步的目录结构解析。
2. 删除非图片文件
delete_non_image_files
函数通过遍历给定子目录下的所有文件,并检查文件的扩展名是否属于指定的图片格式集合,如果不是,则使用 os.remove()
方法将它们删除。它使用了Python集合,这是一个高效的数据结构,用于检查元素是否存在于集合中。
3. 检查并记录问题图片
check_pic
函数尝试打开一个图片文件,如果文件不存在、有问题或者是一个"解压缩炸弹",它们会引发异常。在这种情况下,该函数会将问题文件的路径记录到一个文本文件中,并尝试删除这个图片文件。这个功能提高了数据清洗的效率。
4. 图片文件重命名
重命名部分是通过 rename_image_files_in_subfolder
函数来完成的。它首先会遍历给定子目录下的所有文件,并为图片文件生成新的文件名,新文件名包括子文件夹的名称和一个独一无二的索引。在重命名之前,它会调用 check_pic
来确保图片文件没有问题。如果需要,它也会相应地处理文件名冲突。
5. 递归处理所有文件夹
最后,我们有 rename_image_files_in_subfolders
函数,它递归地处理给定路径下的所有文件夹,对每个子文件夹调用 rename_image_files_in_subfolder
方法。
我想强调一下最后的测试部分。当程序作为主模块运行时,会进行实际的操作,删除所有子文件夹中的非图片文件,并递归地重命名所有的图片文件。这确保了脚本在执行时不会有未预期的行为。
程序源码:
import os from PIL import Image # 获取指定目录下的所有子文件夹 def get_subfolders(path): subfolders = [os.path.join(path, o) for o in os.listdir(path) if os.path.isdir(os.path.join(path, o))] return subfolders # 删除指定目录下的所有子文件夹中不是图片文件的文件 def delete_non_image_files(subfolder): allowed_exts = {'.jpg', '.jpeg', '.png', '.bmp', '.jpe'} for file_name in os.listdir(subfolder): file_path = os.path.join(subfolder, file_name) if os.path.isfile(file_path): file_ext = os.path.splitext(file_name)[1].lower() if file_ext not in allowed_exts: os.remove(file_path) # 如果不是图片格式,就删除这个文件 # 检查图片是否有问题并记录问题图片 def check_pic(path_pic, files_to_delete): try: img = Image.open(path_pic, 'r') img.load() return True except (FileNotFoundError, OSError, Image.DecompressionBombError): f = open('False.txt', 'a+') f.write(str(path_pic) + '\n') f.close() try: os.remove(path_pic) except PermissionError: print('File In Use:'+path_pic) files_to_delete.append(path_pic) except (FileNotFoundError, OSError) as e: print(f"删除文件 {path_pic} 失败: {e}") return False # 重命名指定目录下的所有子文件夹中的图片文件 def rename_image_files_in_subfolder(subfolder, subfolder_name): try: unique_index = 0 files_to_delete = [] # 保存需要删除的文件 for index, file_name in enumerate(os.listdir(subfolder)): file_path = os.path.join(subfolder, file_name) if os.path.isfile(file_path): file_ext = os.path.splitext(file_name)[1].lower() if file_ext in ['.jpg', '.jpeg', '.png', '.bmp', '.jpe']: # 在重命名之前检查图片是否有问题 if check_pic(file_path, files_to_delete): # 如果图片没问题,进行重命名 new_file_name = f"{subfolder_name}_{index+1}{file_ext}" new_file_path = os.path.join(subfolder, new_file_name) while os.path.exists(new_file_path): unique_index += 1 new_file_name = f"{subfolder_name}_{index+1}_{unique_index}{file_ext}" new_file_path = os.path.join(subfolder, new_file_name) os.rename(file_path, new_file_path) except PermissionError: print(f"Error: failed to rename files in subfolder {subfolder}.") # 递归处理所有文件夹 def rename_image_files_in_subfolders(path): for root, dirs, files in os.walk(path): for subfolder in dirs: subfolder_path = os.path.join(root, subfolder) rename_image_files_in_subfolder(subfolder_path, subfolder) # 测试 if __name__ == '__main__': for root, dirs, files in os.walk("."): for subfolder in dirs: subfolder_path = os.path.join(root, subfolder) delete_non_image_files(subfolder_path) rename_image_files_in_subfolders(subfolder_path)
新版本:
import os from PIL import Image, ImageFile # 允许加载截断的图像文件 ImageFile.LOAD_TRUNCATED_IMAGES = True # 定义允许的图片文件扩展名 ALLOWED_EXTS = {'.jpg', '.jpeg', '.png', '.bmp', '.jpe'} MIN_DIMENSION = 224 def clean_and_rename_images(root_path): files_to_delete = [] # 保存因权限问题暂时无法删除的文件 # 遍历目录 for root, dirs, files in os.walk(root_path): for file_name in files: file_path = os.path.join(root, file_name) file_ext = os.path.splitext(file_name)[1].lower() if file_ext in ALLOWED_EXTS: # 如果文件是图片,检查图片是否有问题 if not check_pic(file_path, files_to_delete): continue # 如果图片有问题,跳过重命名 else: # 如果文件不是图片,尝试删除 try: os.remove(file_path) except (PermissionError, FileNotFoundError, OSError) as e: print(f"删除文件 {file_path} 失败: {e}") # 重命名图片文件 for subfolder in get_subfolders(root_path): rename_image_files_in_subfolder(subfolder, os.path.basename(subfolder), files_to_delete) # 尝试删除权限问题下未能删除的文件 for file_path in files_to_delete: try: os.remove(file_path) except (FileNotFoundError, OSError) as e: print(f"删除文件 {file_path} 失败: {e}") # 获取指定目录下的所有子文件夹 def get_subfolders(path): return [os.path.join(path, o) for o in os.listdir(path) if os.path.isdir(os.path.join(path, o))] # 检查图片是否有问题并记录问题图片 def check_pic(path_pic, files_to_delete): try: with Image.open(path_pic) as img: img.verify() # 只用于验证图片,不加载图片数据 with Image.open(path_pic) as img: img.load() # 加载图片数据,确保没有问题 if img.width < MIN_DIMENSION or img.height < MIN_DIMENSION: raise ValueError(f"Image dimensions are too small: {img.size}") return True except (FileNotFoundError, OSError, ValueError) as e: with open('False.txt', 'a+') as f: f.write(str(path_pic) + '\n') try: os.remove(path_pic) except PermissionError: files_to_delete.append(path_pic) return False # 重命名指定目录下的所有图片文件 def rename_image_files_in_subfolder(subfolder, subfolder_name, files_to_delete): unique_index = 0 for file_name in os.listdir(subfolder): file_path = os.path.join(subfolder, file_name) file_ext = os.path.splitext(file_name)[1].lower() if file_ext in ALLOWED_EXTS: # 构建目标文件名,并确保它是唯一的 new_file_name = f"{subfolder_name}_{unique_index + 1}{file_ext}" new_file_path = os.path.join(subfolder, new_file_name) while os.path.exists(new_file_path): # 如果目标文件名存在,递增序号直到找到唯一的文件名 unique_index += 1 new_file_name = f"{subfolder_name}_{unique_index + 1}{file_ext}" new_file_path = os.path.join(subfolder, new_file_name) os.rename(file_path, new_file_path) unique_index += 1 if __name__ == '__main__': clean_and_rename_images(".") # 从当前目录开始处理