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(".") # 从当前目录开始处理


