使用Python实现图片处理工具
本文将详细分析一款基于 wxPython 和 Pillow (PIL) 的简单图片处理工具,包括核心功能的实现与代码的设计思路。这款工具支持图片选择、旋转、合并、压缩,并且具有友好的图形用户界面(GUI)。
全部代码
import wx from PIL import Image import os from io import BytesIO class ImageProcessorFrame(wx.Frame): def __init__(self): super().__init__(parent=None, title='图片处理工具') self.selected_images = [] self.current_image = None self.current_pil_image = None # 存储PIL Image对象 self.init_ui() def init_ui(self): panel = wx.Panel(self) main_sizer = wx.BoxSizer(wx.HORIZONTAL) # 左侧控制面板 left_sizer = wx.BoxSizer(wx.VERTICAL) # 创建按钮 select_btn = wx.Button(panel, label='选择图片') rotate_btn = wx.Button(panel, label='旋转') merge_btn = wx.Button(panel, label='合并') compress_btn = wx.Button(panel, label='压缩') # 创建列表框显示选择的图片 self.list_box = wx.ListBox(panel, size=(200, 300)) # 添加组件到左侧sizer left_sizer.Add(select_btn, 0, wx.ALL | wx.EXPAND, 5) left_sizer.Add(self.list_box, 1, wx.ALL | wx.EXPAND, 5) left_sizer.Add(rotate_btn, 0, wx.ALL | wx.EXPAND, 5) left_sizer.Add(merge_btn, 0, wx.ALL | wx.EXPAND, 5) left_sizer.Add(compress_btn, 0, wx.ALL | wx.EXPAND, 5) # 右侧图片显示区域 right_sizer = wx.BoxSizer(wx.VERTICAL) self.image_display = wx.StaticBitmap(panel, size=(600, 400)) right_sizer.Add(self.image_display, 1, wx.EXPAND | wx.ALL, 5) # 将左右两侧添加到主sizer main_sizer.Add(left_sizer, 0, wx.EXPAND | wx.ALL, 5) main_sizer.Add(right_sizer, 1, wx.EXPAND | wx.ALL, 5) # 绑定事件 select_btn.Bind(wx.EVT_BUTTON, self.on_select) rotate_btn.Bind(wx.EVT_BUTTON, self.on_rotate) merge_btn.Bind(wx.EVT_BUTTON, self.on_merge) compress_btn.Bind(wx.EVT_BUTTON, self.on_compress) self.list_box.Bind(wx.EVT_LISTBOX, self.on_select_image) panel.SetSizer(main_sizer) self.SetSize((900, 600)) self.Centre() def update_image_display(self, pil_image): """更新图片显示""" if pil_image: try: # 确保图片是RGB模式 if pil_image.mode != 'RGB': pil_image = pil_image.convert('RGB') # 获取显示区域的大小 display_size = self.image_display.GetSize() image_size = pil_image.size # 计算缩放比例 ratio = min(display_size[0]/image_size[0], display_size[1]/image_size[1]) new_size = (int(image_size[0] * ratio), int(image_size[1] * ratio)) # 调整图片大小 resized_image = pil_image.resize(new_size, Image.Resampling.LANCZOS) # 转换为wx.Bitmap image_buffer = BytesIO() resized_image.save(image_buffer, format='PNG') image_buffer.seek(0) # 重置缓冲区指针到开始位置 wx_image = wx.Image(image_buffer, type=wx.BITMAP_TYPE_PNG) wx_bitmap = wx_image.ConvertToBitmap() # 更新显示 self.image_display.SetBitmap(wx_bitmap) self.current_image = wx_bitmap self.current_pil_image = pil_image # 刷新显示 self.image_display.Refresh() except Exception as e: wx.MessageBox(f'处理图片时出错: {str(e)}', '错误', wx.OK | wx.ICON_ERROR) # 计算缩放比例 ratio = min(display_size[0]/image_size[0], display_size[1]/image_size[1]) new_size = (int(image_size[0] * ratio), int(image_size[1] * ratio)) # 调整图片大小 resized_image = pil_image.resize(new_size, Image.Resampling.LANCZOS) # 转换为wx.Bitmap image_buffer = BytesIO() resized_image.save(image_buffer, format='PNG') image_buffer.seek(0) # 重置缓冲区指针到开始位置 wx_image = wx.Image(image_buffer, type=wx.BITMAP_TYPE_PNG) wx_bitmap = wx_image.ConvertToBitmap() # 更新显示 self.image_display.SetBitmap(wx_bitmap) self.current_image = wx_bitmap self.current_pil_image = pil_image # 刷新显示 self.image_display.Refresh() def on_select(self, event): with wx.FileDialog(self, "选择图片文件", wildcard="图片文件 (*.jpg;*.png)|*.jpg;*.png", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return paths = fileDialog.GetPaths() self.selected_images.extend(paths) self.list_box.Set([os.path.basename(path) for path in self.selected_images]) def on_select_image(self, event): """当在列表中选择图片时触发""" selection = self.list_box.GetSelection() if selection != wx.NOT_FOUND: try: image_path = self.selected_images[selection] # 使用二进制模式读取图片 with Image.open(image_path) as img: # 创建一个副本以确保图片被完全加载 pil_image = img.copy() self.update_image_display(pil_image) except Exception as e: wx.MessageBox(f'无法打开图片: {str(e)}', '错误', wx.OK | wx.ICON_ERROR) def on_rotate(self, event): """旋转当前显示的图片""" if self.current_pil_image: # 顺时针旋转90度 try: rotated_image = self.current_pil_image.rotate(-90, expand=True) self.update_image_display(rotated_image) except Exception as e: wx.MessageBox(f'旋转图片时出错: {str(e)}', '错误', wx.OK | wx.ICON_ERROR) else: wx.MessageBox('请先选择一张图片', '提示', wx.OK | wx.ICON_INFORMATION) def on_merge(self, event): if len(self.selected_images) < 2: wx.MessageBox('请至少选择两张图片', '提示', wx.OK | wx.ICON_INFORMATION) return max_width = 0 total_height = 0 images = [] for img_path in self.selected_images: img = Image.open(img_path) max_width = max(max_width, img.width) total_height += img.height images.append(img) merged_image = Image.new('RGB', (max_width, total_height)) current_height = 0 for img in images: if img.width < max_width: x_offset = (max_width - img.width) // 2 else: x_offset = 0 merged_image.paste(img, (x_offset, current_height)) current_height += img.height save_path = os.path.join(os.path.dirname(self.selected_images[0]), 'merged.jpg') merged_image.save(save_path) wx.MessageBox(f'图片已合并保存至: {save_path}', '成功', wx.OK | wx.ICON_INFORMATION) def on_compress(self, event): merged_path = os.path.join(os.path.dirname(self.selected_images[0]), 'merged.jpg') if not os.path.exists(merged_path): wx.MessageBox('请先合并图片', '提示', wx.OK | wx.ICON_INFORMATION) return img = Image.open(merged_path) width = int(img.width * 0.5) height = int(img.height * 0.5) compressed_img = img.resize((width, height), Image.Resampling.LANCZOS) save_path = os.path.join(os.path.dirname(merged_path), 'compressed.jpg') compressed_img.save(save_path, quality=85, optimize=True) wx.MessageBox(f'压缩后的图片已保存至: {save_path}', '成功', wx.OK | wx.ICON_INFORMATION) def main(): app = wx.App() frame = ImageProcessorFrame() frame.Show() app.MainLoop() if __name__ == '__main__': main()
功能概述
该工具的主要功能包括:
- 选择图片:从本地文件中选择图片并显示在列表中。
- 图片预览:点击列表项可在右侧区域预览图片。
- 图片旋转:支持顺时针旋转当前显示的图片。
- 图片合并:将多张图片垂直合并为一张新图片。
- 图片压缩:对合并后的图片进行尺寸压缩。
代码结构与模块解析
import wx from PIL import Image import os from io import BytesIO
模块说明
wx:提供图形用户界面支持,用于设计窗口、按钮、列表等组件。
Pillow (PIL):Python图像处理库,支持加载、旋转、缩放、保存图片等功能。
os:用于文件路径操作。
io.BytesIO:内存中的二进制流,用于将 PIL 图片转换为 wx.Bitmap 显示。
主类 ImageProcessorFrame
ImageProcessorFrame 继承自 wx.Frame,是程序的主窗口,负责布局、事件绑定和功能处理。
class ImageProcessorFrame(wx.Frame): def __init__(self): super().__init__(parent=None, title='图片处理工具') self.selected_images = [] # 存储已选择的图片路径 self.current_image = None # 当前显示的图片(wx.Bitmap) self.current_pil_image = None # 当前显示的 PIL 图片对象 self.init_ui()
界面初始化
init_ui 方法负责创建和布局界面组件,包括按钮、列表框和图片显示区域。
def init_ui(self): panel = wx.Panel(self) main_sizer = wx.BoxSizer(wx.HORIZONTAL) # 主布局,水平分布 # 左侧控制面板 left_sizer = wx.BoxSizer(wx.VERTICAL) select_btn = wx.Button(panel, label='选择图片') rotate_btn = wx.Button(panel, label='旋转') merge_btn = wx.Button(panel, label='合并') compress_btn = wx.Button(panel, label='压缩') self.list_box = wx.ListBox(panel, size=(200, 300)) left_sizer.Add(select_btn, 0, wx.ALL | wx.EXPAND, 5) left_sizer.Add(self.list_box, 1, wx.ALL | wx.EXPAND, 5) left_sizer.Add(rotate_btn, 0, wx.ALL | wx.EXPAND, 5) left_sizer.Add(merge_btn, 0, wx.ALL | wx.EXPAND, 5) left_sizer.Add(compress_btn, 0, wx.ALL | wx.EXPAND, 5) # 右侧图片显示区域 right_sizer = wx.BoxSizer(wx.VERTICAL) self.image_display = wx.StaticBitmap(panel, size=(600, 400)) right_sizer.Add(self.image_display, 1, wx.EXPAND | wx.ALL, 5) # 将左右两侧添加到主布局 main_sizer.Add(left_sizer, 0, wx.EXPAND | wx.ALL, 5) main_sizer.Add(right_sizer, 1, wx.EXPAND | wx.ALL, 5) # 绑定按钮事件 select_btn.Bind(wx.EVT_BUTTON, self.on_select) rotate_btn.Bind(wx.EVT_BUTTON, self.on_rotate) merge_btn.Bind(wx.EVT_BUTTON, self.on_merge) compress_btn.Bind(wx.EVT_BUTTON, self.on_compress) self.list_box.Bind(wx.EVT_LISTBOX, self.on_select_image) panel.SetSizer(main_sizer) self.SetSize((900, 600)) self.Centre()
布局细节:
- 左侧为按钮和图片列表框。
- 右侧为图片显示区域。
- 使用 wx.BoxSizer 管理布局,保证界面响应式。
按钮绑定:
- on_select:选择图片并添加到列表。
- on_rotate:旋转当前图片。
- on_merge:合并图片。
- on_compress:压缩图片。
核心功能实现
图片选择
def on_select(self, event): with wx.FileDialog(self, "选择图片文件", wildcard="图片文件 (*.jpg;*.png)|*.jpg;*.png", style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST | wx.FD_MULTIPLE) as fileDialog: if fileDialog.ShowModal() == wx.ID_CANCEL: return paths = fileDialog.GetPaths() # 获取选择的文件路径 self.selected_images.extend(paths) # 添加到已选择列表 self.list_box.Set([os.path.basename(path) for path in self.selected_images]) # 显示文件名
功能:
打开文件对话框,支持多选。
将选择的图片路径存储到 self.selected_images,并更新列表框显示。
图片显示与预览
def update_image_display(self, pil_image): if pil_image: try: if pil_image.mode != 'RGB': pil_image = pil_image.convert('RGB') display_size = self.image_display.GetSize() image_size = pil_image.size ratio = min(display_size[0]/image_size[0], display_size[1]/image_size[1]) new_size = (int(image_size[0] * ratio), int(image_size[1] * ratio)) resized_image = pil_image.resize(new_size, Image.Resampling.LANCZOS) image_buffer = BytesIO() resized_image.save(image_buffer, format='PNG') wx_image = wx.Image(image_buffer, type=wx.BITMAP_TYPE_PNG) wx_bitmap = wx_image.ConvertToBitmap() self.image_display.SetBitmap(wx_bitmap) self.current_image = wx_bitmap self.current_pil_image = pil_image self.image_display.Refresh() except Exception as e: wx.MessageBox(f'处理图片时出错: {str(e)}', '错误', wx.OK | wx.ICON_ERROR)
功能:
将 PIL 图片调整为适合显示区域的大小。
转换为 wx.Bitmap 后显示在 StaticBitmap 中。
图片旋转
def on_rotate(self, event): if self.current_pil_image: try: rotated_image = self.current_pil_image.rotate(-90, expand=True) self.update_image_display(rotated_image) except Exception as e: wx.MessageBox(f'旋转图片时出错: {str(e)}', '错误', wx.OK | wx.ICON_ERROR) else: wx.MessageBox('请先选择一张图片', '提示', wx.OK | wx.ICON_INFORMATION)
功能:
使用 Pillow.Image 的 rotate 方法实现顺时针旋转。
图片合并
def on_merge(self, event): if len(self.selected_images) < 2: wx.MessageBox('请至少选择两张图片', '提示', wx.OK | wx.ICON_INFORMATION) return max_width = 0 total_height = 0 images = [] for img_path in self.selected_images: img = Image.open(img_path) max_width = max(max_width, img.width) total_height += img.height images.append(img) merged_image = Image.new('RGB', (max_width, total_height)) current_height = 0 for img in images: x_offset = (max_width - img.width) // 2 merged_image.paste(img, (x_offset, current_height)) current_height += img.height save_path = os.path.join(os.path.dirname(self.selected_images[0]), 'merged.jpg') merged_image.save(save_path) wx.MessageBox(f'图片已合并保存至: {save_path}', '成功', wx.OK | wx.ICON_INFORMATION)
功能:
- 计算合并后图片的总尺寸。
- 使用 Image.new 创建空白图片。
- 将每张图片逐一粘贴。
图片压缩
def on_compress(self, event): merged_path = os.path.join(os.path.dirname(self.selected_images[0]), 'merged.jpg') if not os.path.exists(merged_path): wx.MessageBox('请先合并图片', '提示', wx.OK | wx.ICON_INFORMATION) return img = Image.open(merged_path) width = int(img.width * 0.5) height = int(img.height * 0.5) compressed_img = img.resize((width, height), Image.Resampling.LANCZOS) save_path = os.path.join(os.path.dirname(merged_path), 'compressed.jpg') compressed_img.save(save_path, quality=85, optimize=True) wx.MessageBox(f'压缩后的图片已保存至: {save_path}', '成功', wx.OK | wx.ICON_INFORMATION)
功能:
将合并后的图片尺寸缩小为原来的 50%。
设置压缩质量为 85%,并保存优化后的图片。
运行结果
到此这篇关于使用Python实现图片处理工具的文章就介绍到这了,更多相关Python图片处理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
最新评论