2025 字
10 分钟

图片转换工具

2025-11-10
2025-11-11

Python第三方库安装#

  • PIL
  • tkinter
#打开命令提示符输入:
pip install PIL tkinter

程序代码#

import os
from PIL import Image
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
import math
# 证件照标准尺寸(单位:毫米)
PHOTO_SIZES = {
"一寸": (25, 35),
"小一寸": (22, 32),
"大一寸": (33, 48),
"两寸": (35, 49),
"小二寸": (35, 45),
"大二寸": (35, 53),
"五寸": (89, 127),
"六寸": (102, 152),
"七寸": (127, 178),
"八寸": (152, 203),
"十寸": (203, 254),
"十二寸": (305, 254),
"自定义": (0, 0) # 自定义尺寸
}
# DPI设置(每英寸点数)
DPI = 300
def mm_to_pixels(mm, dpi=DPI):
"""将毫米转换为像素"""
inches = mm / 25.4
return int(inches * dpi)
def convert_image(input_path, output_path, conversion_type, sizes=None):
"""
图像格式转换函数
参数:
input_path: 输入文件路径
output_path: 输出文件路径
conversion_type: 转换类型 ('jpg_to_ico', 'jpg_to_png', 'png_to_jpg')
sizes: ICO 文件包含的尺寸列表,仅对 jpg_to_ico 转换有效
"""
try:
if conversion_type == 'jpg_to_ico':
if sizes is None:
sizes = [16, 32, 48, 64]
# 打开原始图像
with Image.open(input_path) as img:
# 转换为 RGBA 模式(ICO 格式支持透明度)
if img.mode != 'RGBA':
img = img.convert('RGBA')
# 创建不同尺寸的图像列表
icon_sizes = [(size, size) for size in sizes]
images = []
for size in icon_sizes:
# 调整图像大小并使用高质量的缩略图算法
resized_img = img.resize(size, Image.Resampling.LANCZOS)
images.append(resized_img)
# 保存为 ICO 格式
images[0].save(
output_path,
format='ICO',
append_images=images[1:],
optimize=True
)
elif conversion_type == 'jpg_to_png':
with Image.open(input_path) as img:
# 转换为 RGB 模式(PNG 格式支持透明度,但JPG没有透明度)
if img.mode != 'RGB':
img = img.convert('RGB')
img.save(output_path, format='PNG', optimize=True)
elif conversion_type == 'png_to_jpg':
with Image.open(input_path) as img:
# 转换为 RGB 模式(JPG 不支持透明度)
if img.mode != 'RGB':
# 创建一个白色背景,将透明区域填充为白色
background = Image.new('RGB', img.size, (255, 255, 255))
if img.mode == 'RGBA':
# 使用alpha通道作为掩码
background.paste(img, mask=img.split()[-1])
else:
background.paste(img)
img = background
# 设置JPG质量(0-100),默认75
img.save(output_path, format='JPEG', quality=95, optimize=True)
return True, f"转换成功!文件已保存至: {output_path}"
except Exception as e:
return False, f"转换失败: {str(e)}"
def crop_photo(input_path, output_path, photo_size, dpi=DPI, custom_width=0, custom_height=0):
"""
裁切证件照函数
参数:
input_path: 输入文件路径
output_path: 输出文件路径
photo_size: 证件照尺寸名称
dpi: 每英寸点数
custom_width: 自定义宽度(毫米)
custom_height: 自定义高度(毫米)
"""
try:
# 获取尺寸(毫米)
if photo_size == "自定义":
width_mm, height_mm = custom_width, custom_height
else:
width_mm, height_mm = PHOTO_SIZES[photo_size]
# 转换为像素
width_px = mm_to_pixels(width_mm, dpi)
height_px = mm_to_pixels(height_mm, dpi)
# 打开原始图像
with Image.open(input_path) as img:
# 计算裁切区域
img_width, img_height = img.size
target_ratio = width_px / height_px
img_ratio = img_width / img_height
if img_ratio > target_ratio:
# 图像更宽,裁切左右
new_width = int(img_height * target_ratio)
left = (img_width - new_width) // 2
top = 0
right = left + new_width
bottom = img_height
else:
# 图像更高,裁切上下
new_height = int(img_width / target_ratio)
left = 0
top = (img_height - new_height) // 2
right = img_width
bottom = top + new_height
# 裁切图像
cropped_img = img.crop((left, top, right, bottom))
# 调整到目标尺寸
resized_img = cropped_img.resize((width_px, height_px), Image.Resampling.LANCZOS)
# 保存图像
if output_path.lower().endswith('.jpg') or output_path.lower().endswith('.jpeg'):
if resized_img.mode != 'RGB':
resized_img = resized_img.convert('RGB')
resized_img.save(output_path, format='JPEG', quality=95, optimize=True)
else:
resized_img.save(output_path, optimize=True)
return True, f"裁切成功!{photo_size}照片已保存至: {output_path}"
except Exception as e:
return False, f"裁切失败: {str(e)}"
def select_input_file():
"""选择输入文件"""
# 根据选择的转换类型设置文件过滤器
if tab_control.index(tab_control.select()) == 0: # 格式转换标签
conversion_type = conversion_var.get()
if conversion_type == 'jpg_to_ico' or conversion_type == 'jpg_to_png':
filetypes = [("JPG 文件", "*.jpg;*.jpeg"), ("所有文件", "*.*")]
elif conversion_type == 'png_to_jpg':
filetypes = [("PNG 文件", "*.png"), ("所有文件", "*.*")]
else:
filetypes = [("所有文件", "*.*")]
else: # 证件照裁切标签
filetypes = [("图片文件", "*.jpg;*.jpeg;*.png;*.bmp"), ("所有文件", "*.*")]
file_path = filedialog.askopenfilename(
title="选择输入文件",
filetypes=filetypes
)
if file_path:
entry_input.delete(0, tk.END)
entry_input.insert(0, file_path)
# 自动生成输出路径
base_name = os.path.splitext(file_path)[0]
if tab_control.index(tab_control.select()) == 0: # 格式转换标签
conversion_type = conversion_var.get()
if conversion_type == 'jpg_to_ico':
output_path = base_name + ".ico"
elif conversion_type == 'jpg_to_png':
output_path = base_name + ".png"
elif conversion_type == 'png_to_jpg':
output_path = base_name + ".jpg"
else: # 证件照裁切标签
output_path = base_name + "_cropped.jpg"
entry_output.delete(0, tk.END)
entry_output.insert(0, output_path)
# 更新尺寸选择框的状态
update_size_selection()
def select_output_file():
"""选择输出文件"""
if tab_control.index(tab_control.select()) == 0: # 格式转换标签
conversion_type = conversion_var.get()
if conversion_type == 'jpg_to_ico':
filetypes = [("ICO 文件", "*.ico"), ("所有文件", "*.*")]
defaultextension = ".ico"
elif conversion_type == 'jpg_to_png':
filetypes = [("PNG 文件", "*.png"), ("所有文件", "*.*")]
defaultextension = ".png"
elif conversion_type == 'png_to_jpg':
filetypes = [("JPG 文件", "*.jpg;*.jpeg"), ("所有文件", "*.*")]
defaultextension = ".jpg"
else:
filetypes = [("所有文件", "*.*")]
defaultextension = ""
else: # 证件照裁切标签
filetypes = [("JPG 文件", "*.jpg;*.jpeg"), ("PNG 文件", "*.png"), ("所有文件", "*.*")]
defaultextension = ".jpg"
file_path = filedialog.asksaveasfilename(
title="保存输出文件",
defaultextension=defaultextension,
filetypes=filetypes
)
if file_path:
entry_output.delete(0, tk.END)
entry_output.insert(0, file_path)
def update_size_selection():
"""根据转换类型更新尺寸选择框的状态"""
if tab_control.index(tab_control.select()) == 0: # 格式转换标签
conversion_type = conversion_var.get()
if conversion_type == 'jpg_to_ico':
# 启用尺寸选择框
frame_sizes.pack(padx=10, pady=5, fill="x")
for widget in frame_sizes.winfo_children():
widget.configure(state="normal")
else:
# 隐藏尺寸选择框
frame_sizes.pack_forget()
for widget in frame_sizes.winfo_children():
widget.configure(state="disabled")
else: # 证件照裁切标签
# 隐藏尺寸选择框
frame_sizes.pack_forget()
for widget in frame_sizes.winfo_children():
widget.configure(state="disabled")
def update_custom_size_fields():
"""更新自定义尺寸输入框的状态"""
if photo_size_var.get() == "自定义":
entry_custom_width.config(state="normal")
entry_custom_height.config(state="normal")
else:
entry_custom_width.config(state="disabled")
entry_custom_height.config(state="disabled")
def start_conversion():
"""开始转换或裁切"""
input_path = entry_input.get()
output_path = entry_output.get()
if not input_path:
messagebox.showerror("错误", "请选择输入文件")
return
if not output_path:
messagebox.showerror("错误", "请指定输出文件路径")
return
if tab_control.index(tab_control.select()) == 0: # 格式转换标签
conversion_type = conversion_var.get()
# 对于ICO转换,获取尺寸设置
sizes = []
if conversion_type == 'jpg_to_ico':
if var_16.get(): sizes.append(16)
if var_32.get(): sizes.append(32)
if var_48.get(): sizes.append(48)
if var_64.get(): sizes.append(64)
if var_128.get(): sizes.append(128)
if var_256.get(): sizes.append(256)
if not sizes:
messagebox.showerror("错误", "请至少选择一个图标尺寸")
return
# 执行转换
success, message = convert_image(input_path, output_path, conversion_type, sizes)
else: # 证件照裁切标签
photo_size = photo_size_var.get()
# 获取自定义尺寸
custom_width = 0
custom_height = 0
if photo_size == "自定义":
try:
custom_width = float(entry_custom_width.get())
custom_height = float(entry_custom_height.get())
if custom_width <= 0 or custom_height <= 0:
messagebox.showerror("错误", "自定义尺寸必须大于0")
return
except ValueError:
messagebox.showerror("错误", "请输入有效的自定义尺寸")
return
# 获取DPI设置
try:
dpi = int(dpi_var.get())
if dpi <= 0:
messagebox.showerror("错误", "DPI必须大于0")
return
except ValueError:
messagebox.showerror("错误", "请输入有效的DPI值")
return
# 执行裁切
success, message = crop_photo(
input_path,
output_path,
photo_size,
dpi,
custom_width,
custom_height
)
if success:
messagebox.showinfo("成功", message)
else:
messagebox.showerror("错误", message)
# 创建主窗口
root = tk.Tk()
root.title("高级图像格式转换与证件照裁切工具")
root.geometry("550x600")
root.resizable(False, False)
# 创建标签控件
tab_control = ttk.Notebook(root)
# 格式转换标签
tab_format = ttk.Frame(tab_control)
tab_control.add(tab_format, text='格式转换')
# 证件照裁切标签
tab_photo = ttk.Frame(tab_control)
tab_control.add(tab_photo, text='证件照裁切')
tab_control.pack(expand=1, fill='both', padx=10, pady=10)
# 格式转换标签内容
frame_conversion = tk.LabelFrame(tab_format, text="转换类型", padx=10, pady=10)
frame_conversion.pack(padx=10, pady=5, fill="x")
conversion_var = tk.StringVar(value="jpg_to_ico")
tk.Radiobutton(frame_conversion, text="JPG 转 ICO", variable=conversion_var, value="jpg_to_ico", command=update_size_selection).pack(anchor="w")
tk.Radiobutton(frame_conversion, text="JPG 转 PNG", variable=conversion_var, value="jpg_to_png", command=update_size_selection).pack(anchor="w")
tk.Radiobutton(frame_conversion, text="PNG 转 JPG", variable=conversion_var, value="png_to_jpg", command=update_size_selection).pack(anchor="w")
# 文件选择框架
frame_files = tk.LabelFrame(tab_format, text="文件选择", padx=10, pady=10)
frame_files.pack(padx=10, pady=5, fill="x")
tk.Label(frame_files, text="输入文件:").grid(row=0, column=0, sticky="w", pady=5)
entry_input = tk.Entry(frame_files, width=40)
entry_input.grid(row=0, column=1, padx=5, pady=5)
tk.Button(frame_files, text="浏览...", command=select_input_file).grid(row=0, column=2, padx=5, pady=5)
tk.Label(frame_files, text="输出文件:").grid(row=1, column=0, sticky="w", pady=5)
entry_output = tk.Entry(frame_files, width=40)
entry_output.grid(row=1, column=1, padx=5, pady=5)
tk.Button(frame_files, text="浏览...", command=select_output_file).grid(row=1, column=2, padx=5, pady=5)
# 尺寸选择框架(初始隐藏,仅在JPG转ICO时显示)
frame_sizes = tk.LabelFrame(tab_format, text="图标尺寸", padx=10, pady=10)
var_16 = tk.BooleanVar(value=True)
var_32 = tk.BooleanVar(value=True)
var_48 = tk.BooleanVar(value=True)
var_64 = tk.BooleanVar(value=True)
var_128 = tk.BooleanVar(value=True)
var_256 = tk.BooleanVar(value=True)
tk.Checkbutton(frame_sizes, text="16x16", variable=var_16).grid(row=0, column=0, sticky="w")
tk.Checkbutton(frame_sizes, text="32x32", variable=var_32).grid(row=0, column=1, sticky="w")
tk.Checkbutton(frame_sizes, text="48x48", variable=var_48).grid(row=0, column=2, sticky="w")
tk.Checkbutton(frame_sizes, text="64x64", variable=var_64).grid(row=1, column=0, sticky="w")
tk.Checkbutton(frame_sizes, text="128x128", variable=var_128).grid(row=1, column=1, sticky="w")
tk.Checkbutton(frame_sizes, text="256x256", variable=var_256).grid(row=1, column=2, sticky="w")
# 证件照裁切标签内容
frame_photo_size = tk.LabelFrame(tab_photo, text="证件照尺寸", padx=10, pady=10)
frame_photo_size.pack(padx=10, pady=5, fill="x")
photo_size_var = tk.StringVar(value="一寸")
photo_sizes = list(PHOTO_SIZES.keys())
size_dropdown = ttk.Combobox(frame_photo_size, textvariable=photo_size_var, values=photo_sizes, state="readonly")
size_dropdown.pack(pady=5)
size_dropdown.bind('<<ComboboxSelected>>', lambda e: update_custom_size_fields())
# 自定义尺寸框架
frame_custom_size = tk.LabelFrame(tab_photo, text="自定义尺寸 (毫米)", padx=10, pady=10)
frame_custom_size.pack(padx=10, pady=5, fill="x")
tk.Label(frame_custom_size, text="宽度:").grid(row=0, column=0, sticky="w", pady=5)
entry_custom_width = tk.Entry(frame_custom_size, width=10)
entry_custom_width.grid(row=0, column=1, padx=5, pady=5)
tk.Label(frame_custom_size, text="毫米").grid(row=0, column=2, sticky="w", pady=5)
tk.Label(frame_custom_size, text="高度:").grid(row=1, column=0, sticky="w", pady=5)
entry_custom_height = tk.Entry(frame_custom_size, width=10)
entry_custom_height.grid(row=1, column=1, padx=5, pady=5)
tk.Label(frame_custom_size, text="毫米").grid(row=1, column=2, sticky="w", pady=5)
# DPI设置框架
frame_dpi = tk.LabelFrame(tab_photo, text="DPI 设置", padx=10, pady=10)
frame_dpi.pack(padx=10, pady=5, fill="x")
dpi_var = tk.StringVar(value="300")
tk.Label(frame_dpi, text="每英寸点数:").grid(row=0, column=0, sticky="w", pady=5)
entry_dpi = tk.Entry(frame_dpi, textvariable=dpi_var, width=10)
entry_dpi.grid(row=0, column=1, padx=5, pady=5)
# 文件选择框架(证件照)
frame_photo_files = tk.LabelFrame(tab_photo, text="文件选择", padx=10, pady=10)
frame_photo_files.pack(padx=10, pady=5, fill="x")
tk.Label(frame_photo_files, text="输入文件:").grid(row=0, column=0, sticky="w", pady=5)
entry_photo_input = tk.Entry(frame_photo_files, width=40)
entry_photo_input.grid(row=0, column=1, padx=5, pady=5)
tk.Button(frame_photo_files, text="浏览...", command=select_input_file).grid(row=0, column=2, padx=5, pady=5)
tk.Label(frame_photo_files, text="输出文件:").grid(row=1, column=0, sticky="w", pady=5)
entry_photo_output = tk.Entry(frame_photo_files, width=40)
entry_photo_output.grid(row=1, column=1, padx=5, pady=5)
tk.Button(frame_photo_files, text="浏览...", command=select_output_file).grid(row=1, column=2, padx=5, pady=5)
# 转换按钮
btn_convert = tk.Button(root, text="开始处理", command=start_conversion, bg="#4CAF50", fg="white", font=("Arial", 12))
btn_convert.pack(pady=20)
# 初始化界面
update_size_selection()
update_custom_size_fields()
# 启动主循环
root.mainloop()
图片转换工具
https://blog.levek.top/posts/figer_tp/figeger_transport/
作者
levek
发布于
2025-11-10
许可协议
CC BY-NC-SA 4.0
最后更新于 2025-11-11

目录

封面
Loading ...
Loading ...
0:00 / 0:00