Python中圖片壓縮小工具的開發(fā)與異常解決詳解
一、問題描述
通過Deep Seek寫Python代碼,制作一個(gè)圖片壓縮小工具,結(jié)果發(fā)現(xiàn)豎圖壓縮失敗,然后一直讓它優(yōu)化,修復(fù)這個(gè)問題,結(jié)果還是一直失敗。后面一步步調(diào)試才發(fā)現(xiàn),根本不是橫圖、豎圖的問題,而是某些特定場景(圖片大小、壓縮質(zhì)量)下會(huì)導(dǎo)致死循環(huán),進(jìn)而導(dǎo)致壓縮失敗。
二、問題代碼及修復(fù)
下方while循環(huán)的>=會(huì)導(dǎo)致某些特定場景下出現(xiàn)死循環(huán),將等號去掉即可。
def compress_image(self, input_path, output_path, target_size_kb):
"""壓縮單張圖片到目標(biāo)大小"""
# 打開圖片
img = Image.open(input_path)
# 如果是PNG格式,轉(zhuǎn)換為JPG以獲得更好的壓縮效果
if img.format == 'PNG':
img = img.convert('RGB')
output_path = output_path.rsplit('.', 1)[0] + '.jpg'
# 設(shè)置初始質(zhì)量
quality = 95
min_quality = 10
# 轉(zhuǎn)換為字節(jié)并檢查大小
while quality >= min_quality:
# 保存為字節(jié)數(shù)據(jù)以檢查大小
img.save(output_path, quality=quality, optimize=True)
# 檢查文件大小
file_size_kb = os.path.getsize(output_path) / 1024
if file_size_kb <= target_size_kb:
break
# 質(zhì)量降低步長根據(jù)差距動(dòng)態(tài)調(diào)整
size_ratio = file_size_kb / target_size_kb
quality_reduction = max(5, int(quality * (1 - 1 / size_ratio) / 2))
quality = max(min_quality, quality - quality_reduction)
三、修復(fù)歷程

經(jīng)過上述幾個(gè)來回,發(fā)現(xiàn)問題依然沒有解決,于是我就一步步調(diào)試代碼去了,此時(shí)我以為這個(gè)問題是一步步“優(yōu)化”出來的。
【第一個(gè)版本】



后面看了一下發(fā)現(xiàn),前面七個(gè)版本基本都是類似的處理,估計(jì)我是在第四個(gè)版本的時(shí)候使用了那張豎圖,導(dǎo)致壓縮失敗了,然后以“失敗的結(jié)論”去修復(fù)失敗的問題,結(jié)果就一直失敗。開了深度思考之后,又優(yōu)化了兩個(gè)版本,下面是最后一個(gè)版本。
【最后一個(gè)版本】
給我整笑了





成功了?我圖呢 ?_?
此時(shí)我已經(jīng)不想去糾結(jié)了……反正第一個(gè)版本修改之后能正常使用了
四、正確代碼
第一個(gè)版本修改之后的代碼:
import os
import sys
import tkinter as tk
from tkinter import filedialog, messagebox, ttk
from PIL import Image
import threading
class ImageCompressorApp:
def __init__(self, root):
self.root = root
self.root.title("圖片壓縮工具")
self.root.geometry("500x300")
self.root.resizable(False, False)
# 變量初始化
self.input_folder = tk.StringVar()
self.output_folder = tk.StringVar()
self.target_size = tk.IntVar(value=100) # 默認(rèn)100KB
self.create_widgets()
def create_widgets(self):
# 輸入文件夾選擇
tk.Label(self.root, text="輸入文件夾:").grid(row=0, column=0, padx=10, pady=10, sticky="w")
tk.Entry(self.root, textvariable=self.input_folder, width=40).grid(row=0, column=1, padx=10, pady=10)
tk.Button(self.root, text="瀏覽", command=self.browse_input).grid(row=0, column=2, padx=10, pady=10)
# 輸出文件夾選擇
tk.Label(self.root, text="輸出文件夾:").grid(row=1, column=0, padx=10, pady=10, sticky="w")
tk.Entry(self.root, textvariable=self.output_folder, width=40).grid(row=1, column=1, padx=10, pady=10)
tk.Button(self.root, text="瀏覽", command=self.browse_output).grid(row=1, column=2, padx=10, pady=10)
# 目標(biāo)大小設(shè)置
tk.Label(self.root, text="目標(biāo)大小(KB):").grid(row=2, column=0, padx=10, pady=10, sticky="w")
tk.Scale(self.root, from_=10, to=1000, orient=tk.HORIZONTAL, variable=self.target_size).grid(row=2, column=1,
padx=10, pady=10,
sticky="ew")
# 壓縮按鈕
self.compress_btn = tk.Button(self.root, text="開始壓縮", command=self.start_compression, bg="lightblue")
self.compress_btn.grid(row=3, column=1, padx=10, pady=20)
# 進(jìn)度條
self.progress = ttk.Progressbar(self.root, orient=tk.HORIZONTAL, length=400, mode='indeterminate')
self.progress.grid(row=4, column=0, columnspan=3, padx=10, pady=10)
# 狀態(tài)標(biāo)簽
self.status_label = tk.Label(self.root, text="準(zhǔn)備就緒", relief=tk.SUNKEN, anchor=tk.W)
self.status_label.grid(row=5, column=0, columnspan=3, sticky="ew", padx=10, pady=10)
def browse_input(self):
folder = filedialog.askdirectory()
if folder:
self.input_folder.set(folder)
def browse_output(self):
folder = filedialog.askdirectory()
if folder:
self.output_folder.set(folder)
def start_compression(self):
if not self.input_folder.get() or not self.output_folder.get():
messagebox.showerror("錯(cuò)誤", "請選擇輸入和輸出文件夾")
return
# 在后臺(tái)線程中執(zhí)行壓縮,避免界面凍結(jié)
thread = threading.Thread(target=self.compress_images)
thread.daemon = True
thread.start()
def compress_images(self):
self.compress_btn.config(state=tk.DISABLED)
self.progress.start()
self.status_label.config(text="正在壓縮圖片...")
input_dir = self.input_folder.get()
output_dir = self.output_folder.get()
target_size_kb = self.target_size.get()
# 支持的圖片格式
supported_formats = ('.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp')
try:
# 獲取所有圖片文件
image_files = [f for f in os.listdir(input_dir)
if f.lower().endswith(supported_formats)]
if not image_files:
messagebox.showinfo("信息", "未找到支持的圖片文件")
return
# 創(chuàng)建輸出目錄(如果不存在)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# 處理每張圖片
for i, filename in enumerate(image_files):
self.status_label.config(text=f"正在處理 {i + 1}/{len(image_files)}: {filename}")
self.root.update()
input_path = os.path.join(input_dir, filename)
output_path = os.path.join(output_dir, filename)
# 壓縮圖片
self.compress_image(input_path, output_path, target_size_kb)
self.status_label.config(text=f"完成! 已壓縮 {len(image_files)} 張圖片")
messagebox.showinfo("完成", f"圖片壓縮完成! 共處理 {len(image_files)} 張圖片")
except Exception as e:
messagebox.showerror("錯(cuò)誤", f"壓縮過程中發(fā)生錯(cuò)誤: {str(e)}")
finally:
self.progress.stop()
self.compress_btn.config(state=tk.NORMAL)
def compress_image(self, input_path, output_path, target_size_kb):
"""壓縮單張圖片到目標(biāo)大小"""
# 打開圖片
img = Image.open(input_path)
# 如果是PNG格式,轉(zhuǎn)換為JPG以獲得更好的壓縮效果
if img.format == 'PNG':
img = img.convert('RGB')
output_path = output_path.rsplit('.', 1)[0] + '.jpg'
# 設(shè)置初始質(zhì)量
quality = 95
min_quality = 10
# 轉(zhuǎn)換為字節(jié)并檢查大小
while quality > min_quality:
# 保存為字節(jié)數(shù)據(jù)以檢查大小
img.save(output_path, quality=quality, optimize=True)
# 檢查文件大小
file_size_kb = os.path.getsize(output_path) / 1024
if file_size_kb <= target_size_kb:
break
# 質(zhì)量降低步長根據(jù)差距動(dòng)態(tài)調(diào)整
size_ratio = file_size_kb / target_size_kb
quality_reduction = max(5, int(quality * (1 - 1 / size_ratio) / 2))
quality = max(min_quality, quality - quality_reduction)
def main():
root = tk.Tk()
app = ImageCompressorApp(root)
root.mainloop()
if __name__ == "__main__":
main()

原圖998KB,壓縮后140KB,壓縮格式為jpg,支持批量壓縮(壓縮所選文件夾下的所有圖片)
(P.S. 后續(xù)將發(fā)布進(jìn)一步優(yōu)化的版本)
到此這篇關(guān)于Python中圖片壓縮小工具的開發(fā)與異常解決詳解的文章就介紹到這了,更多相關(guān)Python圖片壓縮內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
pytorch 數(shù)據(jù)處理:定義自己的數(shù)據(jù)集合實(shí)例
今天小編就為大家分享一篇pytorch 數(shù)據(jù)處理:定義自己的數(shù)據(jù)集合實(shí)例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-12-12
基于Python輕松實(shí)現(xiàn)PDF轉(zhuǎn)圖片
PDF文件是我們在日常工作和學(xué)習(xí)中常用的文檔格式之一,但你知道嗎,你可以將PDF文件轉(zhuǎn)換為圖像,讓文檔變得更加生動(dòng)有趣,下面我們就來看看具體的實(shí)現(xiàn)方法吧2023-08-08
python安裝并使用virtualenv管理包的詳細(xì)過程
本文主要介紹了Python的安裝過程和如何使用virtualenv管理包,首先,用戶需要訪問Python官網(wǎng)下載安裝包,并運(yùn)行安裝程序,安裝完成后,在命令行輸入Python,顯示安裝的Python版本號,即表示安裝成功,感興趣的朋友一起看看吧2024-10-10
Python Opencv輪廓常用操作代碼實(shí)例解析
這篇文章主要介紹了Python Opencv輪廓常用操作代碼實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Python matplotlib繪制散點(diǎn)圖的實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Python matplotlib繪制散點(diǎn)圖的相關(guān)資料,所謂散點(diǎn)圖就是反映兩組變量每個(gè)數(shù)據(jù)點(diǎn)的值,并且從散點(diǎn)圖可以看出它們之間的相關(guān)性,需要的朋友可以參考下2021-06-06
python和bash統(tǒng)計(jì)CPU利用率的方法
這篇文章主要介紹了python和bash統(tǒng)計(jì)CPU利用率的方法,涉及Python針對系統(tǒng)硬件信息的讀取技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07

