本文目录导读:

我来为您提供一个批量转换和合并图片的Python脚本,这个脚本支持多种功能,包括格式转换、调整大小、合并等。
使用Pillow(推荐)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
批量转换并合并图片脚本
支持:格式转换、调整大小、水平/垂直合并、网格合并
"""
import os
import sys
from PIL import Image
from pathlib import Path
import argparse
class ImageProcessor:
def __init__(self):
self.supported_formats = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}
def convert_format(self, input_path, output_path, target_format='PNG'):
"""转换图片格式"""
try:
img = Image.open(input_path)
# 处理RGBA模式转换为RGB(保存为JPEG时)
if target_format.upper() == 'JPEG' and img.mode == 'RGBA':
img = img.convert('RGB')
# 确保输出目录存在
Path(output_path).parent.mkdir(parents=True, exist_ok=True)
img.save(output_path, target_format.upper())
print(f"✓ 转换成功: {input_path} -> {output_path}")
return True
except Exception as e:
print(f"✗ 转换失败 {input_path}: {str(e)}")
return False
def resize_image(self, img, target_size=(800, 600), maintain_aspect=True):
"""调整图片大小"""
if maintain_aspect:
img.thumbnail(target_size, Image.Resampling.LANCZOS)
return img
else:
return img.resize(target_size, Image.Resampling.LANCZOS)
def merge_horizontal(self, images, spacing=0):
"""水平合并图片"""
total_width = sum(img.width for img in images) + spacing * (len(images) - 1)
max_height = max(img.height for img in images)
merged_img = Image.new('RGB', (total_width, max_height), (255, 255, 255))
x_offset = 0
for img in images:
# 垂直居中
y_offset = (max_height - img.height) // 2
merged_img.paste(img, (x_offset, y_offset))
x_offset += img.width + spacing
return merged_img
def merge_vertical(self, images, spacing=0):
"""垂直合并图片"""
total_height = sum(img.height for img in images) + spacing * (len(images) - 1)
max_width = max(img.width for img in images)
merged_img = Image.new('RGB', (max_width, total_height), (255, 255, 255))
y_offset = 0
for img in images:
# 水平居中
x_offset = (max_width - img.width) // 2
merged_img.paste(img, (x_offset, y_offset))
y_offset += img.height + spacing
return merged_img
def merge_grid(self, images, cols=2, spacing=0):
"""网格合并图片"""
if not images:
return None
rows = (len(images) + cols - 1) // cols
# 计算每个单元格的大小
cell_width = max(img.width for img in images)
cell_height = max(img.height for img in images)
total_width = cols * cell_width + spacing * (cols - 1)
total_height = rows * cell_height + spacing * (rows - 1)
merged_img = Image.new('RGB', (total_width, total_height), (255, 255, 255))
for idx, img in enumerate(images):
row = idx // cols
col = idx % cols
x = col * (cell_width + spacing)
y = row * (cell_height + spacing)
# 居中放置图片
x_offset = (cell_width - img.width) // 2
y_offset = (cell_height - img.height) // 2
merged_img.paste(img, (x + x_offset, y + y_offset))
return merged_img
def batch_convert(self, input_dir, output_dir, target_format='PNG',
recursive=False, resize=None, merge_mode=None,
merge_cols=2, spacing=0):
"""批量处理图片"""
input_path = Path(input_dir)
output_path = Path(output_dir)
if not input_path.exists():
print(f"错误: 输入目录 {input_dir} 不存在")
return False
# 创建输出目录
output_path.mkdir(parents=True, exist_ok=True)
# 收集图片文件
images_to_merge = []
# 遍历文件
pattern = '**/*' if recursive else '*'
for file_path in input_path.glob(pattern):
if file_path.suffix.lower() in self.supported_formats:
try:
img = Image.open(file_path)
# 调整大小
if resize:
img = self.resize_image(img, tuple(resize))
if merge_mode:
images_to_merge.append((file_path, img))
else:
# 直接转换
output_file = output_path / f"{file_path.stem}.{target_format.lower()}"
img.save(output_file)
print(f"✓ 处理: {file_path.name} -> {output_file.name}")
except Exception as e:
print(f"✗ 处理失败 {file_path.name}: {str(e)}")
# 如果需要合并
if merge_mode and images_to_merge:
images = [img for _, img in images_to_merge]
if merge_mode == 'horizontal':
merged = self.merge_horizontal(images, spacing)
elif merge_mode == 'vertical':
merged = self.merge_vertical(images, spacing)
elif merge_mode == 'grid':
merged = self.merge_grid(images, merge_cols, spacing)
else:
print(f"未知的合并模式: {merge_mode}")
return False
output_file = output_path / f"merged.{target_format.lower()}"
merged.save(output_file)
print(f"✓ 合并完成: {output_file}")
# 删除临时文件(如果不需要保留原图)
# for file_path, _ in images_to_merge:
# file_path.unlink()
return True
def main():
parser = argparse.ArgumentParser(description='批量转换和合并图片工具')
parser.add_argument('input_dir', help='输入目录路径')
parser.add_argument('output_dir', help='输出目录路径')
parser.add_argument('--format', '-f', default='PNG',
choices=['PNG', 'JPEG', 'BMP', 'TIFF', 'WEBP'],
help='目标图片格式(默认:PNG)')
parser.add_argument('--resize', '-r', nargs=2, type=int, metavar=('WIDTH', 'HEIGHT'),
help='调整图片大小,如: 800 600')
parser.add_argument('--merge', '-m', choices=['horizontal', 'vertical', 'grid'],
help='合并模式:horizontal(水平)、vertical(垂直)、grid(网格)')
parser.add_argument('--cols', '-c', type=int, default=2,
help='网格合并的列数(默认:2)')
parser.add_argument('--spacing', '-s', type=int, default=0,
help='图片之间的间距(默认:0)')
parser.add_argument('--recursive', '-R', action='store_true',
help='递归处理子目录')
args = parser.parse_args()
processor = ImageProcessor()
success = processor.batch_convert(
args.input_dir,
args.output_dir,
args.format.upper(),
args.recursive,
args.resize,
args.merge,
args.cols,
args.spacing
)
if success:
print("处理完成!")
else:
print("处理失败!")
sys.exit(1)
if __name__ == '__main__':
main()
使用OpenCV(更高效处理)
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
使用OpenCV的批量图片处理脚本
"""
import cv2
import numpy as np
import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
import argparse
from tqdm import tqdm
class OpenCVImageProcessor:
def __init__(self):
self.supported_extensions = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}
def load_image(self, path):
"""加载图片"""
img = cv2.imread(str(path))
if img is None:
raise ValueError(f"无法加载图片: {path}")
return img
def convert_color(self, img, target_format='PNG'):
"""转换颜色格式"""
if target_format.upper() == 'JPEG':
# JPEG不支持透明度
if img.shape[2] == 4:
img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR)
return img
def resize_image(self, img, width=None, height=None, interpolation=cv2.INTER_AREA):
"""调整图片大小"""
h, w = img.shape[:2]
if width is None and height is None:
return img
if width is None:
ratio = height / h
dim = (int(w * ratio), height)
elif height is None:
ratio = width / w
dim = (width, int(h * ratio))
else:
dim = (width, height)
return cv2.resize(img, dim, interpolation=interpolation)
def merge_horizontal(self, images):
"""水平合并"""
return np.hstack(images)
def merge_vertical(self, images):
"""垂直合并"""
return np.vstack(images)
def merge_grid(self, images, cols=2):
"""网格合并"""
rows = (len(images) + cols - 1) // cols
row_images = []
for i in range(rows):
row_imgs = images[i * cols:(i + 1) * cols]
if len(row_imgs) < cols:
# 如果最后一行不满,创建空白图片填充
h, w = row_imgs[0].shape[:2]
blank = np.ones((h, w, 3), dtype=np.uint8) * 255
while len(row_imgs) < cols:
row_imgs.append(blank)
row_images.append(np.hstack(row_imgs))
return np.vstack(row_images)
def process_single_image(self, file_path, output_dir, target_format, resize=None):
"""处理单张图片"""
try:
img = self.load_image(file_path)
if resize:
img = self.resize_image(img, *resize)
img = self.convert_color(img, target_format)
# 构建输出路径
output_path = output_dir / f"{file_path.stem}.{target_format.lower()}"
output_path.parent.mkdir(parents=True, exist_ok=True)
# 根据格式保存
if target_format.upper() == 'JPEG':
cv2.imwrite(str(output_path), img, [cv2.IMWRITE_JPEG_QUALITY, 95])
elif target_format.upper() == 'PNG':
cv2.imwrite(str(output_path), img, [cv2.IMWRITE_PNG_COMPRESSION, 3])
else:
cv2.imwrite(str(output_path), img)
return True
except Exception as e:
print(f"处理失败 {file_path.name}: {e}")
return False
def batch_process(self, input_dir, output_dir, target_format='PNG',
recursive=False, resize=None, merge_mode=None,
merge_cols=2, num_workers=4):
"""批量处理(支持多线程)"""
input_path = Path(input_dir)
output_path = Path(output_dir)
if not input_path.exists():
print(f"错误: 输入目录不存在: {input_dir}")
return False
# 收集图片文件
pattern = '**/*' if recursive else '*'
files = [f for f in input_path.glob(pattern)
if f.suffix.lower() in self.supported_extensions]
if not files:
print("未找到图片文件")
return False
print(f"找到 {len(files)} 个图片文件")
if merge_mode:
# 合并模式
images = []
for file_path in tqdm(files, desc="加载图片"):
img = self.load_image(file_path)
if resize:
img = self.resize_image(img, *resize)
images.append(img)
print("正在合并图片...")
if merge_mode == 'horizontal':
merged = self.merge_horizontal(images)
elif merge_mode == 'vertical':
merged = self.merge_vertical(images)
elif merge_mode == 'grid':
merged = self.merge_grid(images, merge_cols)
output_file = output_path / f"merged.{target_format.lower()}"
output_path.mkdir(parents=True, exist_ok=True)
cv2.imwrite(str(output_file), merged)
print(f"合并完成: {output_file}")
else:
# 多线程处理
with ThreadPoolExecutor(max_workers=num_workers) as executor:
futures = []
for file_path in files:
future = executor.submit(
self.process_single_image,
file_path, output_path, target_format, resize
)
futures.append(future)
for future in tqdm(futures, desc="处理图片"):
future.result()
return True
def main():
parser = argparse.ArgumentParser(description='使用OpenCV的批量图片处理工具')
parser.add_argument('input_dir', help='输入目录')
parser.add_argument('output_dir', help='输出目录')
parser.add_argument('--format', '-f', default='PNG',
choices=['PNG', 'JPEG', 'BMP', 'TIFF', 'WEBP'],
help='目标格式')
parser.add_argument('--resize', '-r', nargs=2, type=int,
metavar=('WIDTH', 'HEIGHT'),
help='调整大小')
parser.add_argument('--merge', '-m', choices=['horizontal', 'vertical', 'grid'],
help='合并模式')
parser.add_argument('--cols', '-c', type=int, default=2,
help='网格列数')
parser.add_argument('--recursive', '-R', action='store_true',
help='递归处理')
parser.add_argument('--workers', '-w', type=int, default=4,
help='工作线程数')
args = parser.parse_args()
processor = OpenCVImageProcessor()
success = processor.batch_process(
args.input_dir,
args.output_dir,
args.format,
args.recursive,
args.resize,
args.merge,
args.cols,
args.workers
)
if success:
print("处理完成!")
else:
print("处理失败!")
exit(1)
if __name__ == '__main__':
main()
使用示例
安装依赖
# 方案一(Pillow) pip install Pillow # 方案二(OpenCV) pip install opencv-python tqdm
基本用法
# 转换格式 python image_processor.py ./input ./output --format PNG # 调整大小并转换 python image_processor.py ./input ./output --format JPEG --resize 800 600 # 水平合并 python image_processor.py ./input ./output --merge horizontal # 网格合并(3列,间距10像素) python image_processor.py ./input ./output --merge grid --cols 3 --spacing 10 # 递归处理子目录 python image_processor.py ./input ./output --format WEBP --recursive
参数说明
input_dir: 输入图片目录output_dir: 输出目录--format: 目标格式 (PNG/JPEG/BMP/TIFF/WEBP)--resize: 调整大小 (宽度 高度)--merge: 合并模式 (horizontal/vertical/grid)--cols: 网格列数--spacing: 图片间距--recursive: 递归子目录--workers: 工作线程数
这个脚本可以满足您的大多数图片批量处理需求,您可以根据实际需要进行调整。