Python: Making a Bulk Image Compression Script
There are very little tools to do this offline without using unknown executables, so follow this guide to learn in Python!

For some reason, there is very little tools avaliable to do this offline. So here is a very simple way to do this in Python 3!
Libraries
- Pillow -
pip install Pillow
sys
os
Requirements
We need this code to:
- Compress any image file
- Use command line arguments
- Recursively search input folder
- Export all files to a folder
- Losslessly compress
Step-by-Step Code
For this script we want the basic usage to be:
python script.py <format to compress> <format to output> <input folder> <output folder>
We will begin by importing sys, os and Pillow.
from PIL import Image
import sys
import os
# python script.py <format to compress> <format to output> <input folder> <output folder>
To make it easier for us to re-use, lets make a simple class called BulkImageCompress
. We need to setup some variables for compression settings and to pass our arguments to.
class BulkImageCompress:
quality:int = 85
kwargs:dict = {"progressive": True,
"optimize": True}
def __init__(self, in_type, out_type, in_folder, out_folder):
self.in_type = in_type
self.out_type = out_type
self.in_folder = in_folder
self.out_folder = out_folder
bic = BulkImageCompress(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
Now that we have our class setup, we need a method to get all the files from our directory recursively. We will call this get_files
.
def get_files(self):
self.file_list = []
for (directory, directory_names, filenames) in os.walk(self.in_folder):
for filename in filenames:
if filename.endswith(f".{self.in_type}"):
# Append to list as full path
self.file_list.append(os.sep.join([directory, filename]))
Recursive directory walk
Awesome, so now we have a method that creates a list of image files according to our input type and input folder.
So now we need to make a method which will compress our images according to our input type/format. It needs to be able to support .webp
files, so in order to do this we need to change our kwargs passed to the Image.save function.
def compress_image(self, file_path):
img = Image.open(file_path)
# Get just file name
file_name = os.path.basename(file_path)
if self.out_type in ("jpg", "jpeg"):
img = img.convert('RGB') # Remove transparency
self.out_type = "jpeg"
# Create path with output folder, type and file name
output_name = file_name.replace(f".{self.in_type}", f"_compressed. {self.out_type}")
img.save(os.sep.join([self.out_folder, output_name]), # Create full path
self.out_type,
quality=self.quality, # Lossless quality setting
**self.kwargs) # Dynamic arguments based on output type
So this now lets compress a single file, but how do we bulk compress all the files we stored in the variable self.file_list
? We create a simple method called compress_all()
. All this needs to do is loop through our file list and run the compress_image()
each iteration.
def compress_all(self):
for file in self.file_list:
self.compress_image(file)
And thats it, now all you need to do is run the get_files()
and compress_all()
methods in that order.
Full Script
from PIL import Image
import sys
import os
# python script.py <format to compress> <format to output> <input folder> <output folder>
class BulkImageCompress:
quality:int = 85
kwargs:dict = {"progressive": True,
"optimize": True}
def __init__(self, in_type, out_type, in_folder, out_folder):
self.in_type = in_type
self.out_type = out_type
self.in_folder = in_folder
self.out_folder = out_folder
if out_type == "webp":
self.kwargs = {"method": 6, "lossless": False}
def get_files(self):
self.file_list = []
for (directory, directory_names, filenames) in os.walk(self.in_folder):
for filename in filenames:
if filename.endswith(f".{self.in_type}"):
# Append to list as full path
self.file_list.append(os.sep.join([directory, filename]))
def compress_all(self):
for file in self.file_list:
self.compress_image(file)
def compress_image(self, file_path):
img = Image.open(file_path)
# Get just file name
file_name = os.path.basename(file_path)
if self.out_type in ("jpg", "jpeg"):
img = img.convert('RGB') # Remove transparency
self.out_type = "jpeg"
# Create path with output folder, type and file name
output_name = file_name.replace(f".{self.in_type}", f"_compressed.{self.out_type}")
img.save(os.sep.join([self.out_folder, output_name]), # Create full path
self.out_type,
quality=self.quality, # Lossless quality setting
**self.kwargs) # Dynamic arguments based on output type
if __name__ == "__main__":
bic = BulkImageCompress(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4])
bic.get_files()
bic.compress_all()
Feel free to improve on this script on my github, it's a great way to improve your Python and git usage (whilst also building history on your GitHub account!).
Comments ()