Sometimes when sending some files such as PPT and Word, the files are too large because the pictures in the file are too large and cannot be sent. All pictures in the file can be compressed. The following code is compressed according to the user-defined target size (such as 30KB or 40KB) and ensure the sharpness of the picture as much as possible.
High-quality scaling: Use LANCZOS resampling algorithm to ensure the sharpness of the zoomed image.
Sharpening enhancement: Sharpen the image after compression to further improve clarity.
Intelligent judgment: Prioritize reducing quality, avoid unnecessary scaling, and reduce clarity loss.
from PIL import Image, ImageFilter import io def compress_image(input_path, output_path, max_size_kb, quality=85, step=5): """ Compress the picture below the specified size and ensure clarity as much as possible. :param input_path: Enter the image path :param output_path: output image path :param max_size_kb: Target size (unit: KB) :param quality: Initial compression quality (default 85) :param step: each time the mass is reduced (default 5) """ # Open the picture img = (input_path) # Convert the image to RGB mode (if it is RGBA or other mode) if in ('RGBA', 'LA'): img = ('RGB') # Create a byte stream object img_byte_arr = () # Save the image to the byte stream, the initial quality is quality (img_byte_arr, format='JPEG', quality=quality) # Get the current image size current_size = len(img_byte_arr.getvalue()) / 1024 # Convert to KB # If the image size exceeds the maximum limit, gradually reduce the quality while current_size > max_size_kb and quality > 10: quality -= step img_byte_arr = () (img_byte_arr, format='JPEG', quality=quality) current_size = len(img_byte_arr.getvalue()) / 1024 # If the image size still exceeds the maximum limit, adjust the image size while current_size > max_size_kb: width, height = # Calculate the scaling ratio to ensure that the image size is close to the target size scale_factor = (max_size_kb / current_size) ** 0.5 new_width = int(width * scale_factor) new_height = int(height * scale_factor) img = ((new_width, new_height), ) img_byte_arr = () (img_byte_arr, format='JPEG', quality=quality) current_size = len(img_byte_arr.getvalue()) / 1024 # Sharpen the image img = () # Save the compressed picture with open(output_path, 'wb') as f: (f, format='JPEG', quality=quality) print(f"Compressed image size: {current_size:.2f} KB") input_image = r"E:\Desktop\" output_image = r"E:\Desktop\" target_size_kb = 35 # Customize the target size, for example 30KBcompress_image(input_image, output_image, max_size_kb=target_size_kb)
Python+PIL will compress the image just 200KB
Solution
Compress the image to below the target size, and then fill all the difference with "0".
Core content
How to make up for the core content, the following two ideas are provided:
The first type (save):
① Open the image file and convert it to BytesIO
② Calculate the difference (target size - compressed size) and make up with "\x00"
③ Save
The second type (save2):
① cmd generates a file of the specified size
② Write the compressed binary stream to the generated file
File structure
| - - new
| - - old
| - - | - -
Code
#!/usr/bin/env python # -*- coding: utf-8 -*- # # from PIL import Image from io import BytesIO from os import system from os import listdir from os import remove from import getsize from import exists __author__ = 'one-ccs' """ Function: Put ".\old"All pictures in the directory are compressed and filled to 200KB and stored in ".\new" In the directory. """ settings = { 'loadPath': r'.\old', # The path to compressed image 'savePath': r'.\new', # Save the path 'size': 200, 'quality': 90 } class ImageFixCompressor(): def __init__(self) -> None: = None = None = None = 0 = 0 = 0 = 'JPEG' = 200 # Target size (KB) = 90 # Compression ratio If the image is larger than the target size after compression, the value should be reduced. def split_path(self, path:str='') -> tuple: """ Extract the path and file name in the path. """ if not isinstance(path, str): raise ValueError(f'parameter "path" The data type should be "str", But it was passed in "{type(path)}" type.') # Determine whether '/' '\' is used as the directory separator flag = path[::-1].find('/') if flag == -1: flag = path[::-1].find('\\') if flag == -1: raise ValueError(f'parameter "path" The data type should be "str", But it was passed in "{type(path)}" type.') name = path[-flag:] path = path[:-flag] return (path, name) def full_path(self, path) -> str: return fr'{path}\{}' def open(self, imgPath:str) -> None: """ Open the image file :Path: Image file path. """ try: _, = self.split_path(imgPath) = getsize(imgPath) = (imgPath) print(f'Open: "{imgPath}" success; size: { / 1024:.2f} KB.') except Exception as e: print(f'mistake: document "{imgPath}" Open failed; Reason: "{e}".') def show(self) -> None: () def compress(self, format='JPEG', quality=None) -> None: if format == 'PNG' or format == 'png': = 'PNG' if quality: = quality () = BytesIO() (, format=, quality=) def save(self, savePath:str, cover:bool=False, name=None) -> None: if cover: mode = 'wb+' else: mode = 'rb+' try: = () / 1024 # ~ Make up for size for i in range(0, * 1024 - ()): (b'\x00') with open(self.full_path(savePath), mode) as fb: (()) = getsize(self.full_path(savePath)) / 1024 print(fr'save: "{self.full_path(savePath)}" success; 压缩size: {:.2f} KB; savesize: {:.2f} KB.') except Exception as e: print(f'mistake: "{}" Save failed; Reason: "{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'abnormal: document "{savePath}" Already exists, 已放弃save.') return system('@echo off') system(f'fsutil file createnew {self.full_path(savePath)} { * 1024}') try: with open(self.full_path(savePath), 'rb+') as fb: (()) = () / 1024 = getsize(self.full_path(savePath)) / 1024 print(fr'save: "{self.full_path(savePath)}" success; 压缩size: {:.2f} KB; savesize: {:.2f} KB.') except Exception as e: print(f'mistake: "{}" Save failed; Reason: "{e}".') def main(args): compressor = ImageFixCompressor() oldImgPaths = listdir(settings['loadPath']) for oldImgPath in oldImgPaths: fullPath = f"{settings['loadPath']}\{oldImgPath}" (fullPath) () (settings['savePath'], cover=True) # ~ return return 0 if __name__ == '__main__': import sys (main())
This is the end of this article about Python using PIL for image compression. For more related Python PIL compression image content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!