ZHBPythonTool/jpg_to_png_converter.py
张宏博 450edbc9bb init
2025-12-19 16:20:53 +08:00

253 lines
8.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
JPG转PNG格式转换器
功能:
1. 单个文件转换
2. 批量文件夹转换
3. 保持图片质量
4. 可选择是否删除原文件
"""
import os
import sys
from pathlib import Path
from PIL import Image
import argparse
class ImageConverter:
def __init__(self):
"""初始化图像转换器"""
self.supported_formats = ['.jpg', '.jpeg', '.JPG', '.JPEG']
self.converted_count = 0
self.failed_count = 0
self.failed_files = []
def convert_single_image(self, input_path: Path, output_path: Path = None, quality: int = 95) -> bool:
"""
转换单个JPG图片为PNG
Args:
input_path: 输入文件路径
output_path: 输出文件路径(可选)
quality: 图片质量对PNG无效但保留参数
Returns:
bool: 转换是否成功
"""
try:
# 检查输入文件是否存在
if not input_path.exists():
print(f"❌ 文件不存在: {input_path}")
return False
# 检查文件格式
if input_path.suffix.lower() not in [ext.lower() for ext in self.supported_formats]:
print(f"❌ 不支持的格式: {input_path.suffix}")
return False
# 设置输出路径
if output_path is None:
output_path = input_path.with_suffix('.png')
# 确保输出目录存在
output_path.parent.mkdir(parents=True, exist_ok=True)
print(f"🔄 转换中: {input_path.name} -> {output_path.name}")
# 打开并转换图片
with Image.open(input_path) as img:
# 如果是RGBA模式直接保存
# 如果是RGB模式转换为RGBA以支持透明度
if img.mode in ('RGBA', 'LA'):
# 已经有透明通道
img.save(output_path, 'PNG', optimize=True)
else:
# 转换为RGBA模式
if img.mode == 'P':
# 调色板模式转换为RGBA
img = img.convert('RGBA')
elif img.mode in ('RGB', 'L'):
# RGB或灰度模式转换为RGBA
img = img.convert('RGBA')
img.save(output_path, 'PNG', optimize=True)
print(f"✅ 转换成功: {output_path}")
self.converted_count += 1
return True
except Exception as e:
print(f"❌ 转换失败 {input_path}: {e}")
self.failed_count += 1
self.failed_files.append(str(input_path))
return False
def convert_folder(self, folder_path: Path, output_folder: Path = None,
delete_original: bool = False, recursive: bool = True) -> None:
"""
批量转换文件夹中的JPG图片
Args:
folder_path: 输入文件夹路径
output_folder: 输出文件夹路径(可选)
delete_original: 是否删除原文件
recursive: 是否递归处理子文件夹
"""
if not folder_path.exists():
print(f"❌ 文件夹不存在: {folder_path}")
return
if not folder_path.is_dir():
print(f"❌ 不是有效的文件夹: {folder_path}")
return
print(f"📁 开始处理文件夹: {folder_path}")
# 获取所有JPG文件
if recursive:
jpg_files = []
for ext in self.supported_formats:
jpg_files.extend(folder_path.rglob(f"*{ext}"))
else:
jpg_files = []
for ext in self.supported_formats:
jpg_files.extend(folder_path.glob(f"*{ext}"))
if not jpg_files:
print("📷 未找到JPG文件")
return
print(f"📷 找到 {len(jpg_files)} 个JPG文件")
# 转换每个文件
for jpg_file in jpg_files:
# 计算输出路径
if output_folder:
# 保持相对目录结构
relative_path = jpg_file.relative_to(folder_path)
output_path = output_folder / relative_path.with_suffix('.png')
else:
# 在原目录生成PNG文件
output_path = jpg_file.with_suffix('.png')
# 转换文件
success = self.convert_single_image(jpg_file, output_path)
# 如果转换成功且需要删除原文件
if success and delete_original:
try:
jpg_file.unlink()
print(f"🗑️ 已删除原文件: {jpg_file}")
except Exception as e:
print(f"⚠️ 删除原文件失败 {jpg_file}: {e}")
def print_summary(self) -> None:
"""打印转换摘要"""
total = self.converted_count + self.failed_count
print("\n" + "="*50)
print("🎯 转换摘要")
print(f"📊 总文件数: {total}")
print(f"✅ 成功转换: {self.converted_count}")
print(f"❌ 转换失败: {self.failed_count}")
if self.failed_files:
print("\n❌ 失败的文件:")
for file in self.failed_files:
print(f" - {file}")
print("="*50)
def main():
"""主函数"""
parser = argparse.ArgumentParser(description='JPG转PNG格式转换器')
parser.add_argument('input', help='输入文件或文件夹路径')
parser.add_argument('-o', '--output', help='输出文件或文件夹路径(可选)')
parser.add_argument('-d', '--delete', action='store_true', help='转换后删除原文件')
parser.add_argument('-r', '--recursive', action='store_true', default=True, help='递归处理子文件夹(默认启用)')
parser.add_argument('--no-recursive', action='store_true', help='不递归处理子文件夹')
# 如果没有命令行参数,使用交互模式
if len(sys.argv) == 1:
print("🎨 JPG转PNG格式转换器")
print("="*40)
# 输入路径
input_path = input("请输入JPG文件或文件夹路径: ").strip().strip('"')
if not input_path:
print("❌ 未输入路径,退出程序")
return
input_path = Path(input_path)
# 输出路径(可选)
output_input = input("输出路径(留空为原位置转换): ").strip().strip('"')
output_path = Path(output_input) if output_input else None
# 是否删除原文件
delete_original = input("转换后删除原JPG文件吗(y/N): ").strip().lower() == 'y'
# 是否递归处理
if input_path.is_dir():
recursive = input("递归处理子文件夹吗?(Y/n): ").strip().lower() != 'n'
else:
recursive = False
converter = ImageConverter()
if input_path.is_file():
# 单文件转换
converter.convert_single_image(input_path, output_path)
elif input_path.is_dir():
# 文件夹批量转换
converter.convert_folder(input_path, output_path, delete_original, recursive)
else:
print(f"❌ 路径不存在: {input_path}")
return
converter.print_summary()
else:
# 命令行模式
args = parser.parse_args()
input_path = Path(args.input)
output_path = Path(args.output) if args.output else None
delete_original = args.delete
recursive = args.recursive and not args.no_recursive
converter = ImageConverter()
if input_path.is_file():
converter.convert_single_image(input_path, output_path)
elif input_path.is_dir():
converter.convert_folder(input_path, output_path, delete_original, recursive)
else:
print(f"❌ 路径不存在: {input_path}")
return
converter.print_summary()
def quick_convert_demo():
"""快速转换演示函数"""
print("🚀 快速转换模式")
# 示例用法
converter = ImageConverter()
# 转换单个文件
# converter.convert_single_image(Path("example.jpg"))
# 批量转换文件夹
# converter.convert_folder(Path("./images"))
print("请修改代码中的路径后使用")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n❌ 用户中断操作")
except Exception as e:
print(f"❌ 程序运行错误: {e}")