基于Python自制一個圖片批量處理工具實現(xiàn)格式統(tǒng)一和大小壓縮
在日常工作和生活中,我們經(jīng)常需要處理大量圖片——比如電商商品圖需要統(tǒng)一正方形尺寸、公眾號配圖要控制在3MB以內(nèi)、多格式圖片需轉(zhuǎn)為JPG統(tǒng)一管理。手動處理幾十上百張圖效率極低,今天就給大家分享一款自制的Python圖片批量處理工具,無需復雜操作,圖形化界面一鍵搞定所有需求!
工具核心功能
這款工具專為批量圖片處理設計,整合了4個高頻需求,徹底解放雙手:
- 正方形轉(zhuǎn)換:自動將任意尺寸圖片轉(zhuǎn)為正方形,短邊兩側(cè)填充白色背景,避免拉伸變形
- 格式統(tǒng)一:支持PNG、JPG、BMP、GIF、WebP等多種格式,統(tǒng)一輸出為JPG格式
- 大小壓縮:智能控制圖片大小在3MB以內(nèi),自動平衡質(zhì)量與體積,避免失真
- 安全處理:原圖保持不變,處理后的圖片自動保存到新文件夾,杜絕誤操作
環(huán)境準備
1. 基礎環(huán)境
- Python 3.6 及以上版本(推薦3.8+,兼容性更好)
- tkinter 庫(Python標準庫,默認已預裝,無需額外安裝)
2. 依賴安裝
核心依賴僅需Pillow庫(Python圖像處理神器),打開命令行執(zhí)行以下命令:
pip install pillow
如果需要批量安裝,也可以創(chuàng)建requirements.txt文件,寫入以下內(nèi)容后執(zhí)行pip install -r requirements.txt:
pillow>=9.0.0
完整代碼(可直接復制運行)
import os
import sys
from PIL import Image
import time
from io import BytesIO
import tkinter as tk
from tkinter import filedialog, ttk, messagebox
import threading
import queue
# 打包命令:pyinstaller --noconsole --onefile --icon=icon.ico --name="圖片批量處理工具" image_processor.py
class ImageProcessor:
def __init__(self):
self.window = tk.Tk()
self.window.title("圖片批量處理工具")
# 設置窗口大小和位置(居中顯示)
window_width = 800
window_height = 600
screen_width = self.window.winfo_screenwidth()
screen_height = self.window.winfo_screenheight()
x = (screen_width - window_width) // 2
y = (screen_height - window_height) // 2
self.window.geometry(f"{window_width}x{window_height}+{x}+{y}")
self.window.resizable(False, False)
# 界面樣式配置(現(xiàn)代簡潔風格)
self.style = ttk.Style()
self.style.theme_use('clam')
self.style.configure("TButton", padding=10, font=('微軟雅黑', 10))
self.style.configure("TLabel", font=('微軟雅黑', 10))
self.style.configure("Header.TLabel", font=('微軟雅黑', 16, 'bold'))
self.style.configure("Info.TLabel", font=('微軟雅黑', 9))
self.style.configure("Custom.TButton", padding=10, font=('微軟雅黑', 10, 'bold'))
# 線程通信隊列(避免界面卡頓)
self.message_queue = queue.Queue()
self.create_widgets()
self.update_messages()
def create_widgets(self):
# 主框架(統(tǒng)一內(nèi)邊距,布局更整潔)
main_frame = ttk.Frame(self.window, padding="20")
main_frame.pack(fill=tk.BOTH, expand=True)
# 標題區(qū)域
title_frame = ttk.Frame(main_frame)
title_frame.pack(fill=tk.X, pady=(0, 20))
title_label = ttk.Label(title_frame, text="圖片批量處理工具", style="Header.TLabel")
title_label.pack(side=tk.LEFT)
# 功能說明區(qū)域
features_frame = ttk.LabelFrame(main_frame, text="功能說明", padding="10")
features_frame.pack(fill=tk.X, pady=(0, 20))
features_text = """本工具提供以下功能:
? 將圖片轉(zhuǎn)換為正方形(短邊兩側(cè)填充白色)
? 統(tǒng)一轉(zhuǎn)換為JPG格式,保證最佳圖片質(zhì)量
? 智能控制圖片大小在3MB以下
? 自動創(chuàng)建輸出文件夾,原圖保持不變"""
features_label = ttk.Label(features_frame, text=features_text, justify=tk.LEFT)
features_label.pack(pady=5)
# 文件夾選擇區(qū)域
folder_frame = ttk.LabelFrame(main_frame, text="選擇文件夾", padding="10")
folder_frame.pack(fill=tk.X, pady=(0, 20))
self.folder_path = tk.StringVar()
folder_entry = ttk.Entry(folder_frame, textvariable=self.folder_path, width=70)
folder_entry.pack(side=tk.LEFT, padx=(0, 10), fill=tk.X, expand=True)
folder_button = ttk.Button(folder_frame, text="瀏覽...", command=self.select_folder, style="Custom.TButton")
folder_button.pack(side=tk.LEFT)
# 操作按鈕區(qū)域
control_frame = ttk.Frame(main_frame)
control_frame.pack(fill=tk.X, pady=(0, 10))
self.process_button = ttk.Button(control_frame, text="開始處理",
command=self.start_processing,
style="Custom.TButton")
self.process_button.pack(pady=10)
# 進度顯示區(qū)域
progress_frame = ttk.LabelFrame(main_frame, text="處理進度", padding="10")
progress_frame.pack(fill=tk.X, pady=(0, 20))
self.progress_var = tk.DoubleVar()
self.progress_bar = ttk.Progressbar(progress_frame,
variable=self.progress_var,
maximum=100,
length=300)
self.progress_bar.pack(fill=tk.X, pady=(5, 10))
self.progress_label = ttk.Label(progress_frame, text="等待開始...", style="Info.TLabel")
self.progress_label.pack()
# 處理詳情日志區(qū)域
log_frame = ttk.LabelFrame(main_frame, text="處理詳情", padding="10")
log_frame.pack(fill=tk.BOTH, expand=True)
text_frame = ttk.Frame(log_frame)
text_frame.pack(fill=tk.BOTH, expand=True)
self.log_text = tk.Text(text_frame, height=10, width=70,
font=('微軟雅黑', 9),
wrap=tk.WORD)
self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL,
command=self.log_text.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.log_text.configure(yscrollcommand=scrollbar.set)
# 狀態(tài)欄
self.status_label = ttk.Label(main_frame, text="就緒", style="Info.TLabel")
self.status_label.pack(pady=(10, 0))
def select_folder(self):
# 選擇文件夾并更新路徑
folder_path = filedialog.askdirectory(title="選擇包含圖片的文件夾")
if folder_path:
self.folder_path.set(folder_path)
self.log_message(f"已選擇文件夾: {folder_path}")
self.status_label.config(text="已選擇文件夾,可以開始處理")
def log_message(self, message):
# 日志信息入隊(線程安全)
self.message_queue.put(message)
def update_messages(self):
# 實時更新日志顯示
try:
while True:
message = self.message_queue.get_nowait()
self.log_text.insert(tk.END, message + "\n")
self.log_text.see(tk.END)
self.log_text.update()
except queue.Empty:
pass
self.window.after(100, self.update_messages)
def start_processing(self):
# 預處理校驗(避免無效操作)
folder_path = self.folder_path.get()
if not folder_path:
messagebox.showerror("錯誤", "請先選擇要處理的文件夾!")
return
if not os.path.exists(folder_path):
messagebox.showerror("錯誤", "選擇的文件夾不存在!")
return
# 禁用按鈕防止重復點擊
self.process_button.state(['disabled'])
self.status_label.config(text="正在處理中...")
self.progress_label.config(text="正在準備處理...")
# 開啟子線程處理圖片(避免界面凍結(jié))
thread = threading.Thread(target=self.process_images_thread, args=(folder_path,))
thread.daemon = True
thread.start()
def process_images_thread(self, input_folder):
try:
# 支持的圖片格式(覆蓋主流格式)
supported_formats = ['.png', '.jpeg', '.jpg', '.bmp', '.gif', '.webp']
# 創(chuàng)建輸出文件夾(原文件夾名+“調(diào)整”)
dir_name = os.path.basename(input_folder)
output_dir_name = dir_name + "調(diào)整"
parent_dir = os.path.dirname(input_folder)
output_dir = os.path.join(parent_dir, output_dir_name)
try:
os.makedirs(output_dir, exist_ok=True)
except Exception as e:
self.log_message(f"創(chuàng)建輸出文件夾失?。簕str(e)}")
messagebox.showerror("錯誤", f"創(chuàng)建輸出文件夾失敗:{str(e)}")
return
# 篩選圖片文件
image_files = [f for f in os.listdir(input_folder)
if any(f.lower().endswith(fmt) for fmt in supported_formats)]
if not image_files:
self.log_message("沒有找到支持的圖片文件!")
messagebox.showinfo("提示", "文件夾中沒有找到支持的圖片文件!")
return
# 初始化統(tǒng)計參數(shù)
total_files = len(image_files)
processed_files = 0
success_count = 0
error_count = 0
size_reduced_count = 0
total_size_saved = 0
# 日志初始化
self.log_message(f"\n開始處理圖片...")
self.log_message(f"輸入目錄: {input_folder}")
self.log_message(f"輸出目錄: {output_dir}\n")
self.log_message(f"共發(fā)現(xiàn) {total_files} 個圖片文件\n")
# 批量處理圖片
for filename in image_files:
file_path = os.path.join(input_folder, filename)
try:
# 更新進度
processed_files += 1
progress = (processed_files / total_files) * 100
self.progress_var.set(progress)
self.progress_label.config(
text=f"正在處理: {filename} ({processed_files}/{total_files})"
)
# 打開圖片并處理
with Image.open(file_path) as img:
# 保留EXIF信息(避免圖片方向異常)
exif = img.info.get('exif')
# 處理透明背景(PNG轉(zhuǎn)JPG時填充白色)
if img.mode in ('RGBA', 'P'):
background = Image.new('RGB', img.size, 'white')
if img.mode == 'P':
img = img.convert('RGBA')
background.paste(img, mask=img.split()[3] if img.mode == 'RGBA' else None)
img = background
# 轉(zhuǎn)為正方形
square_img = self.convert_to_square(img)
if square_img is None:
continue
# 生成輸出路徑
new_filename = os.path.splitext(filename)[0] + '.jpg'
new_path = os.path.join(output_dir, new_filename)
# 計算大小變化
original_size = os.path.getsize(file_path) / (1024 * 1024)
# 優(yōu)化圖片大?。刂圃?MB內(nèi))
quality = self.optimize_image_size(square_img)
# 保存圖片
save_args = {'format': 'JPEG', 'quality': quality}
if exif:
save_args['exif'] = exif
square_img.save(new_path, **save_args)
# 更新統(tǒng)計信息
new_size = os.path.getsize(new_path) / (1024 * 1024)
success_count += 1
if new_size < original_size:
size_reduced_count += 1
size_reduction = original_size - new_size
total_size_saved += size_reduction
self.log_message(f"? 已處理: {filename} -> {new_filename}")
self.log_message(
f" 大小: {original_size:.1f}MB → {new_size:.1f}MB "
f"(節(jié)省 {size_reduction:.1f}MB)"
)
else:
self.log_message(f"? 已處理: {filename} -> {new_filename}")
self.log_message(f" 大小: {new_size:.1f}MB")
except Exception as e:
error_count += 1
self.log_message(f"? 處理 {filename} 時出錯: {str(e)}")
# 輸出處理總結(jié)
summary = f"""\n處理完成!
----------------------------------------
成功處理:{success_count} 個文件
處理失?。簕error_count} 個文件
文件大小優(yōu)化:{size_reduced_count} 個文件
總共節(jié)省空間:{total_size_saved:.1f}MB
輸出文件夾:{output_dir}
----------------------------------------"""
self.log_message(summary)
# 彈窗提示結(jié)果
if success_count > 0:
messagebox.showinfo("完成",
f"圖片處理完成!\n成功:{success_count} 個\n失?。簕error_count} 個")
else:
messagebox.showwarning("警告", "沒有成功處理任何文件!")
except Exception as e:
self.log_message(f"處理過程出錯:{str(e)}")
messagebox.showerror("錯誤", f"處理過程出錯:{str(e)}")
finally:
# 恢復界面狀態(tài)
self.process_button.state(['!disabled'])
self.progress_var.set(0)
self.progress_label.config(text="處理完成")
self.status_label.config(text="就緒")
def convert_to_square(self, image):
"""將圖片轉(zhuǎn)為正方形,最大尺寸限制800x800(避免過大)"""
try:
width, height = image.size
max_size = max(width, height)
# 縮放過大圖片
if max_size > 800:
scale = 800 / max_size
new_width = int(width * scale)
new_height = int(height * scale)
image = image.resize((new_width, new_height), Image.LANCZOS) # 高質(zhì)量縮放
width, height = image.size
max_size = max(width, height)
# 創(chuàng)建白色正方形背景
square_img = Image.new('RGB', (max_size, max_size), 'white')
# 居中粘貼原圖
paste_x = (max_size - width) // 2 if height > width else 0
paste_y = (max_size - height) // 2 if width > height else 0
square_img.paste(image, (paste_x, paste_y))
return square_img
except Exception as e:
self.log_message(f"轉(zhuǎn)換圖片時出錯:{str(e)}")
return None
def get_file_size(self, img, quality):
"""計算指定質(zhì)量下的圖片大?。ㄗ止?jié))"""
buffer = BytesIO()
img.save(buffer, format='JPEG', quality=quality)
size = buffer.tell()
buffer.close()
return size
def optimize_image_size(self, img, max_size_mb=2.9):
"""二分查找最優(yōu)質(zhì)量,確保圖片<3MB(留0.1MB冗余)"""
max_size_bytes = int(max_size_mb * 1024 * 1024)
quality = 95
current_size = self.get_file_size(img, quality)
# 原圖已達標,直接返回高質(zhì)量
if current_size <= max_size_bytes:
return quality
# 二分查找最優(yōu)質(zhì)量
min_quality = 5
max_quality = 95
best_quality = max_quality
while min_quality <= max_quality:
quality = (min_quality + max_quality) // 2
current_size = self.get_file_size(img, quality)
if current_size > max_size_bytes:
max_quality = quality - 1
else:
best_quality = quality
min_quality = quality + 1
# 最終校驗(確保不超標)
final_size = self.get_file_size(img, best_quality)
while final_size > max_size_bytes and best_quality > 5:
best_quality -= 1
final_size = self.get_file_size(img, best_quality)
return best_quality
def run(self):
# 啟動GUI
self.window.mainloop()
def main():
app = ImageProcessor()
app.run()
if __name__ == '__main__':
main()
使用教程(超簡單!)
運行程序
將上述代碼保存為`image_processor.py
到此這篇關于基于Python自制一個圖片批量處理工具實現(xiàn)格式統(tǒng)一和大小壓縮的文章就介紹到這了,更多相關Python圖片批量處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
這篇文章主要是關于深度學習的簡介,對大家學習了解機器深度學習有一定的幫助,以后會持續(xù)更新本系列,希望能為大家?guī)硪恍┦肇洠屛覀円黄饋砜纯聪旅娴奈恼掳?/div> 2021-04-04
使用Django和Ansible實現(xiàn)自動化部署方式
這篇文章主要介紹了使用Django和Ansible實現(xiàn)自動化部署方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2025-05-05
Centos 升級到python3后pip 無法使用的解決方法
今天小編就為大家分享一篇Centos 升級到python3后pip 無法使用的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-06-06最新評論

