Python利用PIL进行图片压缩

2025-02-14 05:50
文章标签 python 进行 图片 压缩 pil

本文主要是介绍Python利用PIL进行图片压缩,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Python利用PIL进行图片压缩》有时在发送一些文件如PPT、Word时,由于文件中的图片太大,导致文件也太大,无法发送,所以本文为大家介绍了Python中图片压缩的方法,需要的可以参考下...

有时在发送一些文件如PPT、Word时,由于文件中的图片太大,导致文件也太大,无法发送,所有可以对文件中的图片进行压缩,下面代码根据用户自定义的目标大小(如30KB或40KB)进行压缩,并尽可能保证图片清晰度。

高质量缩放:使用LANCZOS重采样算法,保证缩放后的图片清晰度。

锐化增强:在压缩后对图片进行锐化处理,进一步提升清晰度。

智能判断:优先降低质量,避免不必要的缩放,减少清晰度损失。

from PIL import Image, ImageFilter
import io
 
 
def compress_image(input_path, output_path, max_size_kb, quality=85, step=5):
    """
    压缩图片到指定大小以下,并尽可能保证清晰度。
    :param input_path: 输入图片CBeuy路径
    :param output_path: 输出图片路径
    :param max_size_kb: 目标大小(单位:KB)
    :param quality: 初始压缩质量(默认85)
    :param step: 每次降低质量的步长(默认5)
    """
    # 打开图片
    img = Image.open(input_path)
 
    # 将图片转换为RGB模式(如果是RGBA或其他模式)
    if img.mode in ('RGBA', 'LA'):
        img = img.convert('RGB')
 
    # 创建一个字节流对象
    img_byte_arr = io.BytesIO()
 
    # 保存图片到字节流,初始质量为quality
    img.save(img_byte_arr, format='JPEG', quality=quality)
 
    # 获取当前图片android大小
    current_size = len(img_byte_arr.getvalue()) / 1024  # 转换为KB
 
    # 如果图片大小超过最大限制,逐步降低质量
    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
 
    # 如果图片大小仍然超过最大限制,调整图片尺寸
    while current_size > max_size_kb:
        width, height = img.size
        # 计算缩放比例,确保图片大小接近目标大小
        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
 
    #php 对图片进行锐化处理
    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  # 自定义目标大小,例如30KB
compress_image(input_image, output_image, max_size_kb=target_size_kb)

python+PIL将压缩图片刚好 200KB

解决思路

压缩图片至低于目标大小,再把差的部分全部填充 “0”。

核心内容

核心内容是如何补齐,以下提供两种思路:

第一种(save):

① 打开图片文件并转换为 BytesIO

② 计算 (目标大小 - 压缩后大小) 的差值, 并用 “\x00” 补足

③ 保存

第二种(save2):

① cmd 生成一个指定大小的文件

② 将压缩后的二进制流写入生成的文件

文件结构

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 # 目标大小 (KB)
        self.quality = 90     # 压缩比率 若压缩后图片大于目标大小应减小该值

    def split_path(self, path:str='') -> tuple:
        """
        提取 path 中的路径与文件名.
        """
        if not isinstance(path, str):
            raise ValueError(f'参数 "path" 的数据类型应该为 "str", 但是传入了 "{type(path)}" 类型.')

        # 判断是否是以 '/' '\' 作为目录分隔符
        flag = path[::-1].find('/')
        if flag == -1:
            flag = path[::-1].find('\\')
            if flag == -1:
                raise ValueError(f'参数 "path" 的数据类型应该为 "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:stCBeuyr) -> None:
        """
        打开图像文件

        :参数 imgPath: 图片文件路径.
        """
        try:
            _, self.name = self.split_path(imgPath)
            self.oldSize = getsize(imgPath)
            self.image = Image.open(imgPath)

            print(f'打开: "{imgPath}" 成功; 大小: {self.oldSize / 1024:.2f} KB.')
        except Exception as e:
            print(f'错误: 文件 "{imgPath}" 打开失败; 原因: "{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
            # ~ 补齐大小
            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'错误: "{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'错误: "{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))

到此这篇关于Python利用PIL进行图片压缩的文章就介绍到这了,更多相关Python PIL压缩图片内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程China编程(www.chinasem.cn)!

这篇关于Python利用PIL进行图片压缩的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1153426

相关文章

python中字符串拼接的几种方法及优缺点对比详解

《python中字符串拼接的几种方法及优缺点对比详解》在Python中,字符串拼接是常见的操作,Python提供了多种方法来拼接字符串,每种方法有其优缺点和适用场景,以下是几种常见的字符串拼接方法,需... 目录1. 使用 + 运算符示例:优缺点:2. 使用&nbsjsp;join() 方法示例:优缺点:3

如何使用Python实现一个简单的window任务管理器

《如何使用Python实现一个简单的window任务管理器》这篇文章主要为大家详细介绍了如何使用Python实现一个简单的window任务管理器,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 任务管理器效果图完整代码import tkinter as tkfrom tkinter i

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

使用Python和python-pptx构建Markdown到PowerPoint转换器

《使用Python和python-pptx构建Markdown到PowerPoint转换器》在这篇博客中,我们将深入分析一个使用Python开发的应用程序,该程序可以将Markdown文件转换为Pow... 目录引言应用概述代码结构与分析1. 类定义与初始化2. 事件处理3. Markdown 处理4. 转

Python实现常用文本内容提取

《Python实现常用文本内容提取》在日常工作和学习中,我们经常需要从PDF、Word文档中提取文本,本文将介绍如何使用Python编写一个文本内容提取工具,有需要的小伙伴可以参考下... 目录一、引言二、文本内容提取的原理三、文本内容提取的设计四、文本内容提取的实现五、完整代码示例一、引言在日常工作和学

Python实战之屏幕录制功能的实现

《Python实战之屏幕录制功能的实现》屏幕录制,即屏幕捕获,是指将计算机屏幕上的活动记录下来,生成视频文件,本文主要为大家介绍了如何使用Python实现这一功能,希望对大家有所帮助... 目录屏幕录制原理图像捕获音频捕获编码压缩输出保存完整的屏幕录制工具高级功能实时预览增加水印多平台支持屏幕录制原理屏幕

Python实现自动化表单填写功能

《Python实现自动化表单填写功能》在Python中,自动化表单填写可以通过多种库和工具实现,本文将详细介绍常用的自动化表单处理工具,并对它们进行横向比较,可根据需求选择合适的工具,感兴趣的小伙伴跟... 目录1. Selenium简介适用场景示例代码优点缺点2. Playwright简介适用场景示例代码

Python循环缓冲区的应用详解

《Python循环缓冲区的应用详解》循环缓冲区是一个线性缓冲区,逻辑上被视为一个循环的结构,本文主要为大家介绍了Python中循环缓冲区的相关应用,有兴趣的小伙伴可以了解一下... 目录什么是循环缓冲区循环缓冲区的结构python中的循环缓冲区实现运行循环缓冲区循环缓冲区的优势应用案例Python中的实现库

使用Python高效获取网络数据的操作指南

《使用Python高效获取网络数据的操作指南》网络爬虫是一种自动化程序,用于访问和提取网站上的数据,Python是进行网络爬虫开发的理想语言,拥有丰富的库和工具,使得编写和维护爬虫变得简单高效,本文将... 目录网络爬虫的基本概念常用库介绍安装库Requests和BeautifulSoup爬虫开发发送请求解

Python实现html转png的完美方案介绍

《Python实现html转png的完美方案介绍》这篇文章主要为大家详细介绍了如何使用Python实现html转png功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 1.增强稳定性与错误处理建议使用三层异常捕获结构:try: with sync_playwright(