文件夹图像批处理教程

前言

因为经常对图像要做数据清洗,又很费时间去重新写一个,我一直在想能不能写一个通用的脚本或者制作一个可视化的界面对文件夹图像做批量的修改图像大小、重命名、划分数据训练和验证集等等。这里我先介绍一下我因为写过的一些脚本,然后我们对其进行绘总,并制作成一个可视化界面。

脚本

获取图像路径

我们需要一个函数去读取文件夹下的所有文件,获取其文件名,而关于这一部分我很早以前就做过了,详细可以看这里:解决Python读取图片路径存在转义字符

import osdef get_image_path(path):imgfile = []file_list = os.listdir(path)for i in file_list:new_path = os.path.join(path, i).replace("\\", "/")_, file_ext = os.path.splitext(new_path)if file_ext[1:] in ('bmp', 'dng', 'jpeg', 'jpg', 'mpo', 'png', 'tif', 'tiff', 'webp', 'pfm'):imgfile.append(new_path)return natsorted(imgfile)if __name__=="__main__":images_path = r'E:\PythonProject\img_processing_techniques_main\document\images'images_path_list = get_image_path(images_path)print(images_path_list)

我们在这里进行了重命名,请以此篇为准。

批量修改图像大小

def modify_images_size(target_path, target_hwsize, save_path=None):"""批量修改图像大小"""h, w = target_hwsizeimages_paths = get_image_path(target_path)os.makedirs(save_path, exist_ok=True)for i, one_image_path in enumerate(images_paths):try:image = Image.open(one_image_path)resized_image = image.resize((w, h))base_name = os.path.basename(one_image_path)if save_path is not None:new_path = os.path.join(save_path, base_name)else:new_path = one_image_pathresized_image.save(new_path)print(f"Resized {one_image_path} to {new_path}")except Exception as e:print(f"Error resizing {one_image_path}: {e}")

这里的脚本用于图像的批量修改,如果不给保存路径,那么就会修改本地的文件,我们是不推荐这里的,最好还是做好备份。

划分数据集与验证集

def split_train_val_txt(target_path, train_ratio=.8, val_ratio=.2, onlybasename=False):"""如果 train_ratio + val_ratio = 1 表示只划分训练集和验证集, train_ratio + val_ratio < 1表示将剩余的比例划分为测试集"""assert train_ratio + val_ratio <= 1test_ratio = 1. - (train_ratio + val_ratio)images_paths = get_image_path(target_path)num_images = len(images_paths)num_train = round(num_images * train_ratio)num_val = num_images - num_train if test_ratio == 0 else math.ceil(num_images * val_ratio)num_test = 0 if test_ratio == 0 else num_images - (num_train + num_val)with open(os.path.join(target_path, 'train.txt'), 'w') as train_file, \open(os.path.join(target_path, 'val.txt'), 'w') as val_file, \open(os.path.join(target_path, 'test.txt'), 'w') as test_file:for i, image_path in enumerate(images_paths):if onlybasename:image_name, _ = os.path.splitext(os.path.basename(image_path))else:image_name = image_pathif i < num_train:train_file.write(f"{image_name}\n")elif i < num_train + num_val:val_file.write(f"{image_name}\n")else:test_file.write(f"{image_name}\n")print(f"Successfully split {num_images} images into {num_train} train, {num_val} val, and {num_test} test.")

我在这里修改了划分测试集的逻辑,根据划分比例来评判,以免出现使用向上取整或向下取整导致出现的问题(测试集比例不为0,验证集比例按照向上取整划分)。

复制图像到另外一个文件夹

def copy_images_to_directory(target_path, save_folder, message=True):"""复制整个文件夹(图像)到另外一个文件夹"""try:os.makedirs(save_folder, exist_ok=True)source_path = get_image_path(target_path)for img_path in source_path:base_file_name = os.path.basename(img_path)destination_path = os.path.join(save_folder, base_file_name)shutil.copy2(img_path, destination_path)if message:print(f"Successfully copied folder: {img_path} to {save_folder}")except Exception as e:print(f"Error copying folder, {e}")

这个本来是一个小功能,但是我想的是有时候如果要做每张图的匹配,可以修改为将符合条件的路径复制到目标文件夹中。修改也只需加一个列表的判断即可。

获取数据集的均值标准化

def get_dataset_mean_std(train_data):train_loader = DataLoader(train_data, batch_size=1, shuffle=False, num_workers=0,pin_memory=True)mean = torch.zeros(3)std = torch.zeros(3)for im, _ in train_loader:for d in range(3):mean[d] += im[:, d, :, :].mean()std[d] += im[:, d, :, :].std()mean.div_(len(train_data))std.div_(len(train_data))return list(mean.numpy()), list(std.numpy())def get_images_mean_std(target_path):images_paths = get_image_path(target_path)num_images = len(images_paths)mean_sum = np.zeros(3)std_sum = np.zeros(3)for one_image_path in images_paths:pil_image = Image.open(one_image_path).convert("RGB")img_asarray = np.asarray(pil_image) / 255.0individual_mean = np.mean(img_asarray, axis=(0, 1))individual_stdev = np.std(img_asarray, axis=(0, 1))mean_sum += individual_meanstd_sum += individual_stdevmean = mean_sum / num_imagesstd = std_sum / num_imagesreturn mean.astype(np.float32), std.astype(np.float32)

都是相同的方法获取RGB图像的均值标准化,我们更建议直接使用第二个。

批量修改图像后缀名

def modify_images_suffix(target_path, format='png'):"""批量修改图像文件后缀"""images_paths = get_image_path(target_path)for i, one_image_path in enumerate(images_paths):base_name, ext = os.path.splitext(one_image_path)new_path = base_name + '.' + formatos.rename(one_image_path, new_path)print(f"Converting {one_image_path} to {new_path}")

这里仅仅是修改图像的后缀,至于这种强力的修改是否会对图像的格式造成影响我们不做考虑。

批量重命名图像

def batch_rename_images(target_path,save_path,start_index=None,prefix=None,suffix=None,format=None,num_type=1,
):"""重命名图像文件夹中的所有图像文件并保存到指定文件夹:param target_path: 目标文件路径:param save_path: 文件夹的保存路径:param start_index: 默认为 1, 从多少号开始:param prefix: 重命名的通用格式前缀, 如 rename001.png, rename002.png...:param suffix: 重命名的通用格式后缀, 如 001rename.png, 002rename.png...:param format (str): 新的后缀名,不需要包含点(.):param num_type: 数字长度, 比如 3 表示 005:param message: 是否打印修改信息"""os.makedirs(save_path, exist_ok=True)images_paths = get_image_path(target_path)current_num = start_index if start_index is not None else 1for i, image_path in enumerate(images_paths):image_name = os.path.basename(image_path)name, ext = os.path.splitext(image_name)if format is None:ext = extelse:ext = f'.{format}'padded_i = str(current_num).zfill(num_type)if prefix and suffix:new_image_name = f"{prefix}{padded_i}{suffix}{ext}"elif prefix:new_image_name = f"{prefix}{padded_i}{ext}"elif suffix:new_image_name = f"{padded_i}{suffix}{ext}"else:new_image_name = f"{padded_i}{ext}"new_path = os.path.join(save_path, new_image_name)current_num += 1print(f"{i + 1} Successfully rename {image_path} to {new_path}")shutil.copy(image_path, new_path)print("Batch renaming and saving of files completed!")

我们在这里添加了重命名的功能,其实发现很多都可以套在这里面来,所以后面我们修改过后会做成ui进行显示。

完整脚本

下面是我经过测试后的一个脚本,大家可以直接拿去使用。

import os
import math
import torch
import numpy as np
from PIL import Image
import shutil
from torch.utils.data import DataLoader
from natsort import natsorteddef get_image_path(path):imgfile = []file_list = os.listdir(path)for i in file_list:new_path = os.path.join(path, i).replace("\\", "/")_, file_ext = os.path.splitext(new_path)if file_ext[1:] in ('bmp', 'dng', 'jpeg', 'jpg', 'mpo', 'png', 'tif', 'tiff', 'webp', 'pfm'):imgfile.append(new_path)return natsorted(imgfile)def modify_images_size(target_path, target_hwsize, save_path=None):"""批量修改图像大小"""h, w = target_hwsizeimages_paths = get_image_path(target_path)os.makedirs(save_path, exist_ok=True)for i, one_image_path in enumerate(images_paths):try:image = Image.open(one_image_path)resized_image = image.resize((w, h))base_name = os.path.basename(one_image_path)if save_path is not None:new_path = os.path.join(save_path, base_name)else:new_path = one_image_pathresized_image.save(new_path)print(f"Resized {one_image_path} to {new_path}")except Exception as e:print(f"Error resizing {one_image_path}: {e}")def split_train_val_txt(target_path, train_ratio=.8, val_ratio=.2, onlybasename=False):"""如果 train_ratio + val_ratio = 1 表示只划分训练集和验证集, train_ratio + val_ratio < 1表示将剩余的比例划分为测试集"""assert train_ratio + val_ratio <= 1test_ratio = 1. - (train_ratio + val_ratio)images_paths = get_image_path(target_path)num_images = len(images_paths)num_train = round(num_images * train_ratio)num_val = num_images - num_train if test_ratio == 0 else math.ceil(num_images * val_ratio)num_test = 0 if test_ratio == 0 else num_images - (num_train + num_val)with open(os.path.join(target_path, 'train.txt'), 'w') as train_file, \open(os.path.join(target_path, 'val.txt'), 'w') as val_file, \open(os.path.join(target_path, 'test.txt'), 'w') as test_file:for i, image_path in enumerate(images_paths):if onlybasename:image_name, _ = os.path.splitext(os.path.basename(image_path))else:image_name = image_pathif i < num_train:train_file.write(f"{image_name}\n")elif i < num_train + num_val:val_file.write(f"{image_name}\n")else:test_file.write(f"{image_name}\n")print(f"Successfully split {num_images} images into {num_train} train, {num_val} val, and {num_test} test.")def copy_images_to_directory(target_path, save_folder):"""复制整个文件夹(图像)到另外一个文件夹"""try:os.makedirs(save_folder, exist_ok=True)source_path = get_image_path(target_path)for img_path in source_path:base_file_name = os.path.basename(img_path)destination_path = os.path.join(save_folder, base_file_name)shutil.copy2(img_path, destination_path)print(f"Successfully copied folder: {img_path} to {save_folder}")except Exception as e:print(f"Error copying folder, {e}")def get_dataset_mean_std(train_data):train_loader = DataLoader(train_data, batch_size=1, shuffle=False, num_workers=0,pin_memory=True)mean = torch.zeros(3)std = torch.zeros(3)for im, _ in train_loader:for d in range(3):mean[d] += im[:, d, :, :].mean()std[d] += im[:, d, :, :].std()mean.div_(len(train_data))std.div_(len(train_data))return list(mean.numpy()), list(std.numpy())def get_images_mean_std(target_path):images_paths = get_image_path(target_path)num_images = len(images_paths)mean_sum = np.zeros(3)std_sum = np.zeros(3)for one_image_path in images_paths:pil_image = Image.open(one_image_path).convert("RGB")img_asarray = np.asarray(pil_image) / 255.0individual_mean = np.mean(img_asarray, axis=(0, 1))individual_stdev = np.std(img_asarray, axis=(0, 1))mean_sum += individual_meanstd_sum += individual_stdevmean = mean_sum / num_imagesstd = std_sum / num_imagesreturn mean.astype(np.float32), std.astype(np.float32)def modify_images_suffix(target_path, format='png'):"""批量修改图像文件后缀"""images_paths = get_image_path(target_path)for i, one_image_path in enumerate(images_paths):base_name, ext = os.path.splitext(one_image_path)new_path = base_name + '.' + formatos.rename(one_image_path, new_path)print(f"Converting {one_image_path} to {new_path}")def batch_rename_images(target_path,save_path,start_index=None,prefix=None,suffix=None,format=None,num_type=1,
):"""重命名图像文件夹中的所有图像文件并保存到指定文件夹:param target_path: 目标文件路径:param save_path: 文件夹的保存路径:param start_index: 默认为 1, 从多少号开始:param prefix: 重命名的通用格式前缀, 如 rename001.png, rename002.png...:param suffix: 重命名的通用格式后缀, 如 001rename.png, 002rename.png...:param format (str): 新的后缀名,不需要包含点(.):param num_type: 数字长度, 比如 3 表示 005:param message: 是否打印修改信息"""os.makedirs(save_path, exist_ok=True)images_paths = get_image_path(target_path)current_num = start_index if start_index is not None else 1for i, image_path in enumerate(images_paths):image_name = os.path.basename(image_path)name, ext = os.path.splitext(image_name)if format is None:ext = extelse:ext = f'.{format}'padded_i = str(current_num).zfill(num_type)if prefix and suffix:new_image_name = f"{prefix}{padded_i}{suffix}{ext}"elif prefix:new_image_name = f"{prefix}{padded_i}{ext}"elif suffix:new_image_name = f"{padded_i}{suffix}{ext}"else:new_image_name = f"{padded_i}{ext}"new_path = os.path.join(save_path, new_image_name)current_num += 1print(f"{i + 1} Successfully rename {image_path} to {new_path}")shutil.copy(image_path, new_path)print("Batch renaming and saving of files completed!")
if __name__=="__main__":images_path = r'E:\PythonProject\img_processing_techniques_main\document\images'images_path_list = get_image_path(images_path)save_path =r'./save_path'# modify_images_size(images_path, (512, 512), save_path)# print(images_path_list)# split_train_val_txt(images_path, .8, .2)# copy_images_to_directory(images_path, save_folder='./save_path2')# mean, std = get_images_mean_std(images_path)# print(mean, std)# modify_images_suffix('./save_path2')

BatchVision的可视化设计

我将前面的一些脚本制作成了一个批量文件处理系统 BatchVision,可实现批量修改图像大小,划分训练集和验证集,以及批量重命名,其他的一些小功能例如灰度化处理和计算均值标准化。

完整项目请看此处:UI-Design-System-Based-on-PyQt5

这里通过网盘链接分享的离线使用文件exe,无需安装环境: BatchVision

我们设计的可视化界面如下图所示:

其中的一些设定我都是经过严格的判断,如果还有问题可以联系我修改。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/bicheng/82323.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Unity实战笔记】第二十四 · 使用 SMB+Animator 实现基础战斗系统

转载请注明出处&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/146409453 作者&#xff1a;CSDN|Ringleader| 1 结构 1.1 状态机 1.2 SMB 2 代码实现 2.1 核心控制 Player_Base_SMB 继承 StateMachineBehaviour &#xff0c;控制变量初始…

Python虚拟环境再PyCharm中自由切换使用方法

Python开发中的环境隔离是必不可少的步骤,通过使用虚拟环境可以有效地管理不同项目间的依赖,避免包冲突和环境污染。虚拟环境是Python官方提供的一种独立运行环境,每个项目可以拥有自己单独的环境,不同项目之间的环境互不影响。在日常开发中,结合PyCharm这样强大的IDE进行…

大模型智能体入门扫盲——基于camel的概述

前言 本篇博客想带读者进行一个智能体入门扫盲&#xff0c;了解基础知识&#xff0c;为什么用camel呢&#xff0c;因为小洛发现它们文档对这种智能体的基本组件介绍得很全面深入。 基础概念 agent 一个典型的agent智能体包含三个核心部分&#xff1a; 感知模块&#xff1…

目标检测 RT-DETR(2023)详细解读

文章目录 主干网络&#xff1a;Encoder&#xff1a;不确定性最小Query选择Decoder网络&#xff1a; 将DETR扩展到实时场景&#xff0c;提高了模型的检测速度。网络架构分为三部分组成&#xff1a;主干网络、混合编码器、带有辅助预测头的变换器编码器。具体来说&#xff0c;先利…

DeepSeek 赋能数字农业:从智慧种植到产业升级的全链条革新

目录 一、数字农业的现状与挑战二、DeepSeek 技术解析2.1 DeepSeek 的技术原理与优势2.2 DeepSeek 在人工智能领域的地位与影响力 三、DeepSeek 在数字农业中的应用场景3.1 精准种植决策3.2 病虫害监测与防治3.3 智能灌溉与施肥管理3.4 农产品质量追溯与品牌建设 四、DeepSeek …

<uniapp><vuex><状态管理>在uniapp中,如何使用vuex实现数据共享与传递?

前言 本专栏是基于uniapp实现手机端各种小功能的程序&#xff0c;并且基于各种通讯协议如http、websocekt等&#xff0c;实现手机端作为客户端&#xff08;或者是手持机、PDA等&#xff09;&#xff0c;与服务端进行数据通讯的实例开发。 发文平台 CSDN 环境配置 系统&…

高速串行差分信号仿真分析及技术发展挑战续

7.3 3.125Gbps 差分串行信号设计实例仿真分析 7.3.1 设计用例说明 介绍完 Cadence 系统本身所具有的高速差分信号的仿真分析功能之后&#xff0c;我们以一个实例来说明 3.125Gbps 以下的高速差分系统的仿真分析方法。 在网上下载的设计文件“Booksi_Demo_Allegro160_Finishe…

【Golang】部分语法格式和规则

1、时间字符串和时间戳的相互转换 func main() {t1 : int64(1546926630) // 外部传入的时间戳&#xff08;秒为单位&#xff09;&#xff0c;必须为int64类型t2 : "2019-01-08 13:50:30" // 外部传入的时间字符串//时间转换的模板&#xff0c;golang里面只能是 &quo…

第十六章:数据治理之数据架构:数据模型和数据流转关系

本章我们说一下数据架构&#xff0c;说到数据架构&#xff0c;就很自然的想到企业架构、业务架构、软件架构&#xff0c;因为个人并没有对这些内容进行深入了解&#xff0c;所以这里不做比对是否有相似或者共通的地方&#xff0c;仅仅来说一下我理解的数据架构。 1、什么是架构…

Day126 | 灵神 | 二叉树 | 层数最深的叶子结点的和

Day126 | 灵神 | 二叉树 | 层数最深的叶子结点的和 1302.层数最深的叶子结点的和 1302. 层数最深叶子节点的和 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 这道题用层序遍历的思路比较好想&#xff0c;就把每层的都算一下&#xff0c;然后返回最后一层的和就…

PCIE 4.0 vs PCIE 5.0固态硬盘——区别、科普与选购场景全解析

随着数字内容和高性能计算需求的爆发&#xff0c;固态硬盘&#xff08;SSD&#xff09;已成为PC、游戏主机和工作站不可或缺的核心硬件。面对市面上层出不穷的新一代SSD产品&#xff0c;大家最常见的一个疑惑&#xff1a;**PCIe 4.0和PCIe 5.0固态硬盘&#xff0c;到底有啥区别…

vue pinia 独立维护,仓库统一导出

它允许您跨组件/页面共享状态 持久化 安装依赖pnpm i pinia-plugin-persistedstate 将插件添加到 pinia 实例上 pinia独立维护 统一导出 import { createPinia } from pinia import piniaPluginPersistedstate from pinia-plugin-persistedstateconst pinia creat…

Dify源码学习

文章目录 1 大模型基本原理1.1 model_context_tokens、max_tokens和prompt_tokens1.1.1 三者之间的关系1.1.2 总结对比 2 Dify源代码2.0 前后端代码跑起来【0】准备开发环境【1】下载代码【2】运行后端&#xff08;1&#xff09;Start the docker-compose stack&#xff08;2&a…

连接表、视图和存储过程

1. 视图 1.1. 视图的概念 视图&#xff08;View&#xff09;&#xff1a;虚拟表&#xff0c;本身不存储数据&#xff0c;而是封装了一个 SQL 查询的结果集。 用途&#xff1a; 只显示部分数据&#xff0c;提高数据访问的安全性。简化复杂查询&#xff0c;提高复用性和可维护…

微信小程序中,解决lottie动画在真机不显示的问题

api部分 export function getRainInfo() {return onlineRequest({url: /ball/recruit/getRainInfo,method: get}); }data存储json数据 data&#xff1a;{rainJson:{} }onLoad方法获取json数据 onLoad(options) {let that thisgetRainInfo().then((res)>{that.setData({r…

从加密到信任|密码重塑车路云一体化安全生态

目录 一、密码技术的核心支撑 二、典型应用案例 三、未来发展方向 总结 车路云系统涉及海量实时数据交互&#xff0c;包括车辆位置、传感器信息、用户身份等敏感数据。其安全风险呈现三大特征&#xff1a; 开放环境威胁&#xff1a;V2X&#xff08;车与万物互联&#xff0…

光谱相机在地质勘测中的应用

一、‌矿物识别与蚀变带分析‌ ‌光谱特征捕捉‌ 通过可见光至近红外&#xff08;400-1000nm&#xff09;的高光谱分辨率&#xff08;可达3.5nm&#xff09;&#xff0c;精确识别矿物的“光谱指纹”。例如&#xff1a; ‌铜矿‌&#xff1a;在400-500nm波段反射率显著低于围…

理论篇三:如何编写自定义的Webpack Loader或Plugin插件

在 Webpack 中,自定义 Loader 和 Plugin 是扩展构建能力的关键方式。以下是它们的实现方法和核心逻辑,通过代码示例和步骤拆解帮助你快速掌握。 一、自定义 Loader 1. Loader 的本质 作用:将非 JS 文件转换为 Webpack 能处理的模块。特点:纯函数,接收源文件内容,返回处理…

【算法】力扣体系分类

第一章 算法基础题型 1.1 排序算法题 1.1.1 冒泡排序相关题 冒泡排序是一种简单的排序算法&#xff0c;它重复地走访过要排序的数列&#xff0c;一次比较两个元素&#xff0c;如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换&#xff0c…

C11 日期时间处理案例

文章目录 显示当前日期时间得到当前日期时间的17位数字形式(YYYYmmddHHMMSSsss)从日期时间字符串得到time_t 类型时间戳从时期时间字符串得到毫秒单位的时间戳得到当前日期时间以毫秒为单位的时间戳一个综合案例 所有例子在VS2019上编译运行通过 显示当前日期时间 #include &…