2025 字
10 分钟
图片转换工具
Python第三方库安装
- PIL
- tkinter
#打开命令提示符输入:pip install PIL tkinter程序代码
import osfrom PIL import Imageimport tkinter as tkfrom tkinter import filedialog, messagebox, ttkimport 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() 最后更新于 2025-11-11