Python利用PIL進(jìn)行圖片壓縮
有時(shí)在發(fā)送一些文件如PPT、Word時(shí),由于文件中的圖片太大,導(dǎo)致文件也太大,無(wú)法發(fā)送,所有可以對(duì)文件中的圖片進(jìn)行壓縮,下面代碼根據(jù)用戶自定義的目標(biāo)大?。ㄈ?0KB或40KB)進(jìn)行壓縮,并盡可能保證圖片清晰度。
高質(zhì)量縮放:使用LANCZOS重采樣算法,保證縮放后的圖片清晰度。
銳化增強(qiáng):在壓縮后對(duì)圖片進(jìn)行銳化處理,進(jìn)一步提升清晰度。
智能判斷:優(yōu)先降低質(zhì)量,避免不必要的縮放,減少清晰度損失。
from PIL import Image, ImageFilter
import io
def compress_image(input_path, output_path, max_size_kb, quality=85, step=5):
"""
壓縮圖片到指定大小以下,并盡可能保證清晰度。
:param input_path: 輸入圖片路徑
:param output_path: 輸出圖片路徑
:param max_size_kb: 目標(biāo)大?。▎挝唬篕B)
:param quality: 初始?jí)嚎s質(zhì)量(默認(rèn)85)
:param step: 每次降低質(zhì)量的步長(zhǎng)(默認(rèn)5)
"""
# 打開(kāi)圖片
img = Image.open(input_path)
# 將圖片轉(zhuǎn)換為RGB模式(如果是RGBA或其他模式)
if img.mode in ('RGBA', 'LA'):
img = img.convert('RGB')
# 創(chuàng)建一個(gè)字節(jié)流對(duì)象
img_byte_arr = io.BytesIO()
# 保存圖片到字節(jié)流,初始質(zhì)量為quality
img.save(img_byte_arr, format='JPEG', quality=quality)
# 獲取當(dāng)前圖片大小
current_size = len(img_byte_arr.getvalue()) / 1024 # 轉(zhuǎn)換為KB
# 如果圖片大小超過(guò)最大限制,逐步降低質(zhì)量
while current_size > max_size_kb and quality > 10:
quality -= step
img_byte_arr = io.BytesIO()
img.save(img_byte_arr, format='JPEG', quality=quality)
current_size = len(img_byte_arr.getvalue()) / 1024
# 如果圖片大小仍然超過(guò)最大限制,調(diào)整圖片尺寸
while current_size > max_size_kb:
width, height = img.size
# 計(jì)算縮放比例,確保圖片大小接近目標(biāo)大小
scale_factor = (max_size_kb / current_size) ** 0.5
new_width = int(width * scale_factor)
new_height = int(height * scale_factor)
img = img.resize((new_width, new_height), Image.Resampling.LANCZOS)
img_byte_arr = io.BytesIO()
img.save(img_byte_arr, format='JPEG', quality=quality)
current_size = len(img_byte_arr.getvalue()) / 1024
# 對(duì)圖片進(jìn)行銳化處理
img = img.filter(ImageFilter.SHARPEN)
# 保存壓縮后的圖片
with open(output_path, 'wb') as f:
img.save(f, format='JPEG', quality=quality)
print(f"壓縮后的圖片大小: {current_size:.2f} KB")
input_image = r"E:\桌面\2.jpg"
output_image = r"E:\桌面\2-1.jpg"
target_size_kb = 35 # 自定義目標(biāo)大小,例如30KB
compress_image(input_image, output_image, max_size_kb=target_size_kb)Python+PIL將壓縮圖片剛好 200KB
解決思路
壓縮圖片至低于目標(biāo)大小,再把差的部分全部填充 “0”。
核心內(nèi)容
核心內(nèi)容是如何補(bǔ)齊,以下提供兩種思路:
第一種(save):
① 打開(kāi)圖片文件并轉(zhuǎn)換為 BytesIO
② 計(jì)算 (目標(biāo)大小 - 壓縮后大小) 的差值, 并用 “\x00” 補(bǔ)足
③ 保存
第二種(save2):
① cmd 生成一個(gè)指定大小的文件
② 將壓縮后的二進(jìn)制流寫(xiě)入生成的文件
文件結(jié)構(gòu)
main.py
| - - new
| - - old
| - - | - - test.jpg
代碼
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# main.py
from PIL import Image
from io import BytesIO
from os import system
from os import listdir
from os import remove
from os.path import getsize
from os.path import exists
__author__ = 'one-ccs'
"""
功能: 把 ".\old" 目錄下的所有圖片壓縮并填充至 200KB 后存放在 ".\new" 目錄下.
"""
settings = {
'loadPath': r'.\old', # 待壓縮圖片路徑
'savePath': r'.\new', # 保存路徑
'size': 200,
'quality': 90
}
class ImageFixCompressor():
def __init__(self) -> None:
self.imgBytes = None
self.imgPath = None
self.name = None
self.oldSize = 0
self.newSize = 0
self.saveSize = 0
self.format = 'JPEG'
self.targetSize = 200 # 目標(biāo)大小 (KB)
self.quality = 90 # 壓縮比率 若壓縮后圖片大于目標(biāo)大小應(yīng)減小該值
def split_path(self, path:str='') -> tuple:
"""
提取 path 中的路徑與文件名.
"""
if not isinstance(path, str):
raise ValueError(f'參數(shù) "path" 的數(shù)據(jù)類型應(yīng)該為 "str", 但是傳入了 "{type(path)}" 類型.')
# 判斷是否是以 '/' '\' 作為目錄分隔符
flag = path[::-1].find('/')
if flag == -1:
flag = path[::-1].find('\\')
if flag == -1:
raise ValueError(f'參數(shù) "path" 的數(shù)據(jù)類型應(yīng)該為 "str", 但是傳入了 "{type(path)}" 類型.')
name = path[-flag:]
path = path[:-flag]
return (path, name)
def full_path(self, path) -> str:
return fr'{path}\{self.name}'
def open(self, imgPath:str) -> None:
"""
打開(kāi)圖像文件
:參數(shù) imgPath: 圖片文件路徑.
"""
try:
_, self.name = self.split_path(imgPath)
self.oldSize = getsize(imgPath)
self.image = Image.open(imgPath)
print(f'打開(kāi): "{imgPath}" 成功; 大小: {self.oldSize / 1024:.2f} KB.')
except Exception as e:
print(f'錯(cuò)誤: 文件 "{imgPath}" 打開(kāi)失敗; 原因: "{e}".')
def show(self) -> None:
self.image.show()
def compress(self, format='JPEG', quality=None) -> None:
if format == 'PNG' or format == 'png':
self.format = 'PNG'
if quality:
self.quality = quality
self.image.tobytes()
self.imageBuffer = BytesIO()
self.image.save(self.imageBuffer, format=self.format, quality=self.quality)
def save(self, savePath:str, cover:bool=False, name=None) -> None:
if cover:
mode = 'wb+'
else:
mode = 'rb+'
try:
self.newSize = self.imageBuffer.tell() / 1024
# ~ 補(bǔ)齊大小
for i in range(0, self.targetSize * 1024 - self.imageBuffer.tell()):
self.imageBuffer.write(b'\x00')
with open(self.full_path(savePath), mode) as fb:
fb.write(self.imageBuffer.getvalue())
self.saveSize = getsize(self.full_path(savePath)) / 1024
print(fr'保存: "{self.full_path(savePath)}" 成功; 壓縮大小: {self.newSize:.2f} KB; 保存大小: {self.saveSize:.2f} KB.')
except Exception as e:
print(f'錯(cuò)誤: "{self.name}" 保存失敗; 原因: "{e}".')
def save2(self, savePath:str, cover:bool=False, name=None) -> None:
if cover:
if exists(self.full_path(savePath)):
remove(self.full_path(savePath))
else:
print(f'異常: 文件 "{savePath}" 已存在, 已放棄保存.')
return
system('@echo off')
system(f'fsutil file createnew {self.full_path(savePath)} {self.targetSize * 1024}')
try:
with open(self.full_path(savePath), 'rb+') as fb:
fb.write(self.imageBuffer.getvalue())
self.newSize = self.imageBuffer.tell() / 1024
self.saveSize = getsize(self.full_path(savePath)) / 1024
print(fr'保存: "{self.full_path(savePath)}" 成功; 壓縮大小: {self.newSize:.2f} KB; 保存大小: {self.saveSize:.2f} KB.')
except Exception as e:
print(f'錯(cuò)誤: "{self.name}" 保存失敗; 原因: "{e}".')
def main(args):
compressor = ImageFixCompressor()
oldImgPaths = listdir(settings['loadPath'])
for oldImgPath in oldImgPaths:
fullPath = f"{settings['loadPath']}\{oldImgPath}"
compressor.open(fullPath)
compressor.compress()
compressor.save(settings['savePath'], cover=True)
# ~ return
return 0
if __name__ == '__main__':
import sys
sys.exit(main(sys.argv))到此這篇關(guān)于Python利用PIL進(jìn)行圖片壓縮的文章就介紹到這了,更多相關(guān)Python PIL壓縮圖片內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python獲取100以內(nèi)的質(zhì)數(shù)3種方式總結(jié)
質(zhì)數(shù)也稱為質(zhì)數(shù),具有無(wú)限個(gè),質(zhì)數(shù)定義為大于1的自然數(shù),除了1和它本省外沒(méi)有其他因數(shù)的數(shù)稱為質(zhì)數(shù),這篇文章主要給大家介紹了關(guān)于python獲取100以內(nèi)質(zhì)數(shù)的3種方式,需要的朋友可以參考下2024-08-08
Python命令行定時(shí)任務(wù)自動(dòng)化工作流程
本文介紹如何使用Python編寫(xiě)定時(shí)任務(wù),以自動(dòng)執(zhí)行命令行任務(wù)。您將學(xué)習(xí)如何安排定期的任務(wù),處理任務(wù)結(jié)果,以及如何使用Python自動(dòng)化工作流程,從而提高工作效率。無(wú)需手動(dòng)執(zhí)行重復(fù)任務(wù),Python幫您搞定2023-04-04
Python socket如何實(shí)現(xiàn)服務(wù)端和客戶端數(shù)據(jù)傳輸(TCP)
這篇文章主要介紹了Python socket如何實(shí)現(xiàn)服務(wù)端和客戶端數(shù)據(jù)傳輸(TCP),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
Python3如何實(shí)現(xiàn)列表模糊匹配列表
這篇文章主要介紹了Python3如何實(shí)現(xiàn)列表模糊匹配列表,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-11-11
Python如何實(shí)現(xiàn)游戲中的推送通知與消息
文章介紹了如何使用Python構(gòu)建高效的游戲消息推送系統(tǒng),包括使用異步IO和事件驅(qū)動(dòng)編程,以及與Redis、WebSocket等技術(shù)的結(jié)合,文章還強(qiáng)調(diào)了安全性和用戶體驗(yàn)的重要性,并提供了性能優(yōu)化的建議2025-01-01
使用python檢測(cè)主機(jī)存活端口及檢查存活主機(jī)
這篇文章主要介紹了使用python檢測(cè)主機(jī)存活端口及檢查存活主機(jī)的相關(guān)資料,需要的朋友可以參考下2015-10-10
python實(shí)現(xiàn)對(duì)列表中的元素進(jìn)行倒序打印
今天小編就為大家分享一篇python實(shí)現(xiàn)對(duì)列表中的元素進(jìn)行倒序打印,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-11-11

