使用Python實(shí)現(xiàn)圖片處理工具
本文將詳細(xì)分析一款基于 wxPython 和 Pillow (PIL) 的簡(jiǎn)單圖片處理工具,包括核心功能的實(shí)現(xiàn)與代碼的設(shè)計(jì)思路。這款工具支持圖片選擇、旋轉(zhuǎn)、合并、壓縮,并且具有友好的圖形用戶(hù)界面(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 # 存儲(chǔ)PIL Image對(duì)象 self.init_ui() def init_ui(self): panel = wx.Panel(self) main_sizer = wx.BoxSizer(wx.HORIZONTAL) # 左側(cè)控制面板 left_sizer = wx.BoxSizer(wx.VERTICAL) # 創(chuàng)建按鈕 select_btn = wx.Button(panel, label='選擇圖片') rotate_btn = wx.Button(panel, label='旋轉(zhuǎn)') merge_btn = wx.Button(panel, label='合并') compress_btn = wx.Button(panel, label='壓縮') # 創(chuàng)建列表框顯示選擇的圖片 self.list_box = wx.ListBox(panel, size=(200, 300)) # 添加組件到左側(cè)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) # 右側(cè)圖片顯示區(qū)域 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) # 將左右兩側(cè)添加到主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') # 獲取顯示區(qū)域的大小 display_size = self.image_display.GetSize() image_size = pil_image.size # 計(jì)算縮放比例 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)) # 調(diào)整圖片大小 resized_image = pil_image.resize(new_size, Image.Resampling.LANCZOS) # 轉(zhuǎn)換為wx.Bitmap image_buffer = BytesIO() resized_image.save(image_buffer, format='PNG') image_buffer.seek(0) # 重置緩沖區(qū)指針到開(kāi)始位置 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'處理圖片時(shí)出錯(cuò): {str(e)}', '錯(cuò)誤', wx.OK | wx.ICON_ERROR) # 計(jì)算縮放比例 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)) # 調(diào)整圖片大小 resized_image = pil_image.resize(new_size, Image.Resampling.LANCZOS) # 轉(zhuǎn)換為wx.Bitmap image_buffer = BytesIO() resized_image.save(image_buffer, format='PNG') image_buffer.seek(0) # 重置緩沖區(qū)指針到開(kāi)始位置 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): """當(dāng)在列表中選擇圖片時(shí)觸發(fā)""" selection = self.list_box.GetSelection() if selection != wx.NOT_FOUND: try: image_path = self.selected_images[selection] # 使用二進(jìn)制模式讀取圖片 with Image.open(image_path) as img: # 創(chuàng)建一個(gè)副本以確保圖片被完全加載 pil_image = img.copy() self.update_image_display(pil_image) except Exception as e: wx.MessageBox(f'無(wú)法打開(kāi)圖片: {str(e)}', '錯(cuò)誤', wx.OK | wx.ICON_ERROR) def on_rotate(self, event): """旋轉(zhuǎn)當(dāng)前顯示的圖片""" if self.current_pil_image: # 順時(shí)針旋轉(zhuǎn)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'旋轉(zhuǎn)圖片時(shí)出錯(cuò): {str(e)}', '錯(cuò)誤', wx.OK | wx.ICON_ERROR) else: wx.MessageBox('請(qǐng)先選擇一張圖片', '提示', wx.OK | wx.ICON_INFORMATION) def on_merge(self, event): if len(self.selected_images) < 2: wx.MessageBox('請(qǐng)至少選擇兩張圖片', '提示', 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('請(qǐng)先合并圖片', '提示', 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()
功能概述
該工具的主要功能包括:
- 選擇圖片:從本地文件中選擇圖片并顯示在列表中。
- 圖片預(yù)覽:點(diǎn)擊列表項(xiàng)可在右側(cè)區(qū)域預(yù)覽圖片。
- 圖片旋轉(zhuǎn):支持順時(shí)針旋轉(zhuǎn)當(dāng)前顯示的圖片。
- 圖片合并:將多張圖片垂直合并為一張新圖片。
- 圖片壓縮:對(duì)合并后的圖片進(jìn)行尺寸壓縮。
代碼結(jié)構(gòu)與模塊解析
import wx from PIL import Image import os from io import BytesIO
模塊說(shuō)明
wx:提供圖形用戶(hù)界面支持,用于設(shè)計(jì)窗口、按鈕、列表等組件。
Pillow (PIL):Python圖像處理庫(kù),支持加載、旋轉(zhuǎn)、縮放、保存圖片等功能。
os:用于文件路徑操作。
io.BytesIO:內(nèi)存中的二進(jìn)制流,用于將 PIL 圖片轉(zhuǎn)換為 wx.Bitmap 顯示。
主類(lèi) ImageProcessorFrame
ImageProcessorFrame 繼承自 wx.Frame,是程序的主窗口,負(fù)責(zé)布局、事件綁定和功能處理。
class ImageProcessorFrame(wx.Frame): def __init__(self): super().__init__(parent=None, title='圖片處理工具') self.selected_images = [] # 存儲(chǔ)已選擇的圖片路徑 self.current_image = None # 當(dāng)前顯示的圖片(wx.Bitmap) self.current_pil_image = None # 當(dāng)前顯示的 PIL 圖片對(duì)象 self.init_ui()
界面初始化
init_ui 方法負(fù)責(zé)創(chuàng)建和布局界面組件,包括按鈕、列表框和圖片顯示區(qū)域。
def init_ui(self): panel = wx.Panel(self) main_sizer = wx.BoxSizer(wx.HORIZONTAL) # 主布局,水平分布 # 左側(cè)控制面板 left_sizer = wx.BoxSizer(wx.VERTICAL) select_btn = wx.Button(panel, label='選擇圖片') rotate_btn = wx.Button(panel, label='旋轉(zhuǎn)') 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) # 右側(cè)圖片顯示區(qū)域 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) # 將左右兩側(cè)添加到主布局 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()
布局細(xì)節(jié):
- 左側(cè)為按鈕和圖片列表框。
- 右側(cè)為圖片顯示區(qū)域。
- 使用 wx.BoxSizer 管理布局,保證界面響應(yīng)式。
按鈕綁定:
- on_select:選擇圖片并添加到列表。
- on_rotate:旋轉(zhuǎn)當(dāng)前圖片。
- on_merge:合并圖片。
- on_compress:壓縮圖片。
核心功能實(shí)現(xiàn)
圖片選擇
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]) # 顯示文件名
功能:
打開(kāi)文件對(duì)話框,支持多選。
將選擇的圖片路徑存儲(chǔ)到 self.selected_images,并更新列表框顯示。
圖片顯示與預(yù)覽
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'處理圖片時(shí)出錯(cuò): {str(e)}', '錯(cuò)誤', wx.OK | wx.ICON_ERROR)
功能:
將 PIL 圖片調(diào)整為適合顯示區(qū)域的大小。
轉(zhuǎn)換為 wx.Bitmap 后顯示在 StaticBitmap 中。
圖片旋轉(zhuǎn)
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'旋轉(zhuǎn)圖片時(shí)出錯(cuò): {str(e)}', '錯(cuò)誤', wx.OK | wx.ICON_ERROR) else: wx.MessageBox('請(qǐng)先選擇一張圖片', '提示', wx.OK | wx.ICON_INFORMATION)
功能:
使用 Pillow.Image 的 rotate 方法實(shí)現(xiàn)順時(shí)針旋轉(zhuǎn)。
圖片合并
def on_merge(self, event): if len(self.selected_images) < 2: wx.MessageBox('請(qǐng)至少選擇兩張圖片', '提示', 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)
功能:
- 計(jì)算合并后圖片的總尺寸。
- 使用 Image.new 創(chuàng)建空白圖片。
- 將每張圖片逐一粘貼。
圖片壓縮
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('請(qǐng)先合并圖片', '提示', 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)
功能:
將合并后的圖片尺寸縮小為原來(lái)的 50%。
設(shè)置壓縮質(zhì)量為 85%,并保存優(yōu)化后的圖片。
運(yùn)行結(jié)果
到此這篇關(guān)于使用Python實(shí)現(xiàn)圖片處理工具的文章就介紹到這了,更多相關(guān)Python圖片處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Python使用random模塊實(shí)現(xiàn)擲骰子游戲的示例代碼
這篇文章主要介紹了Python使用random模塊實(shí)現(xiàn)擲骰子游戲的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04Python Vaex實(shí)現(xiàn)快速分析100G大數(shù)據(jù)量
Vaex是一個(gè)開(kāi)源的DataFrame庫(kù),它可以對(duì)表格數(shù)據(jù)集進(jìn)行可視化、探索、分析,甚至機(jī)器學(xué)習(xí),這些數(shù)據(jù)集和你的硬盤(pán)驅(qū)動(dòng)器一樣大。本文就來(lái)聊聊如何利用Vaex實(shí)現(xiàn)快速分析100G大數(shù)據(jù)量,需要的可以參考一下2023-03-03python人工智能tensorflow函數(shù)np.random模塊使用
這篇文章主要為大家介紹了python人工智能tensorflow函數(shù)np.random模塊使用方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-05-05python教程之用py2exe將PY文件轉(zhuǎn)成EXE文件
py2exe是一個(gè)將python腳本轉(zhuǎn)換成windows上的可獨(dú)立執(zhí)行的可執(zhí)行程序(*.exe)的工具,這樣,你就可以不用裝python而在windows系統(tǒng)上運(yùn)行這個(gè)可執(zhí)行程序。2014-06-06Python使用grequests(gevent+requests)并發(fā)發(fā)送請(qǐng)求過(guò)程解析
這篇文章主要介紹了Python使用grequests并發(fā)發(fā)送請(qǐng)求過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09