批量转换并合并图片的脚本

wen 实用脚本 2

本文目录导读:

批量转换并合并图片的脚本

  1. 方案一:使用Pillow(推荐)
  2. 方案二:使用OpenCV(更高效处理)
  3. 使用示例

我来为您提供一个批量转换和合并图片的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: 工作线程数

这个脚本可以满足您的大多数图片批量处理需求,您可以根据实际需要进行调整。

抱歉,评论功能暂时关闭!