本文目录导读:

我来为您提供一个批量转换和合并图片的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: 工作线程数
这个脚本可以满足您的大多数图片批量处理需求,您可以根据实际需要进行调整。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播