How to Reduce a Jpeg Size to a 'Desired Size'

How to reduce a jpeg size to a 'desired size'?

I am still learning Python, so there may be better ways, but here is a function that saves a PIL/Pillow image as a JPEG and allows you to specify a maximum size.

It uses a binary search to minimise the amount of work needed and it encodes into BytesIO memory buffer to save writing images to disk. If anyone has any suggestions for improvements, please let me know!

#!/usr/local/bin/python3

import io
import math
import sys
import numpy as np
from PIL import Image

def JPEGSaveWithTargetSize(im, filename, target):
"""Save the image as JPEG with the given name at best quality that makes less than "target" bytes"""
# Min and Max quality
Qmin, Qmax = 25, 96
# Highest acceptable quality found
Qacc = -1
while Qmin <= Qmax:
m = math.floor((Qmin + Qmax) / 2)

# Encode into memory and get size
buffer = io.BytesIO()
im.save(buffer, format="JPEG", quality=m)
s = buffer.getbuffer().nbytes

if s <= target:
Qacc = m
Qmin = m + 1
elif s > target:
Qmax = m - 1

# Write to disk at the defined quality
if Qacc > -1:
im.save(filename, format="JPEG", quality=Qacc)
else:
print("ERROR: No acceptble quality factor found", file=sys.stderr)

################################################################################
# main
################################################################################

# Load sample image
im = Image.open('/Users/mark/sample/images/lena.png')

# Save at best quality under 100,000 bytes
JPEGSaveWithTargetSize(im, "result.jpg", 100000)

If I run that as is, with target size of 100,000 bytes, I get:

-rw-r--r--@   1 mark  staff     96835 11 Sep 18:21 result.jpg

If I change the target size to 50,000 bytes, I get:

-rw-r--r--@   1 mark  staff     49532 11 Sep 18:26 result.jpg

Keywords: Python, PIL, Pillow, JPEG, quality, quality setting, max size, maximum size, image, image processing, binary search.

How to reduce the file size on JPEG images in batch (/Mac)?

This is an example from the command line using convert (brew info imagemagick) converting all *.jpg images in one directory to .png:

$ for i in *.jpg; do
convert "$i" "${i%.jpg}.png"
done

To test before (dry-run) you could use echo instead of the <command>:

$ for i in *.jpg; do
echo "$i" "${i%.jpg}.png"
done

This will search for files within the directory having the extension .jpg then execute the command convert passing as arguments the file name $i and then using as an output the same file name removing the extension and adding the new one .png, this is done using:

"${i%.jpg}.png"

The use of double quotes " is for the case file could contain spaces, check this for more details: shell parameter expansion

For example, to just change the quality of the file you could use:

convert "$i" -quality 80% "${i%.jpg}-new.jpg"

Or if no need to keep the original:

mogrify -quality 80% *.jpg

The main difference is that ‘convert‘ tends to be for working on individual images, whereas ‘mogrify‘ is for batch processing multiple files.

Programmatically Reducing JPEG file size

Another way to reduce image size is to change compression level. You can do that using ImageWriter.

    ImageWriter writer = null;
Iterator<ImageWriter> iwi = ImageIO.getImageWritersByFormatName("jpg");
if (!iwi.hasNext())
return;
writer = (ImageWriter) iwi.next();
ImageWriteParam iwp = writer.getDefaultWriteParam();
iwp.setCompressionMode(ImageWriteParam.MODE_EXPLICIT) ;
iwp.setCompressionQuality(compressionQuality);
writer.setOutput(...);
writer.write(null, image, iwp);

Downscaling image file size using imageMagick

You would use -define jpeg:extent=....

Here is an example with a large image of random data that would need a very large file size to accurately represent it with any reasonable quality.

convert -size 10000x1000 xc:gray +noise random -define jpeg:extent=2MB out.jpg

Result

-rw-r--r--    1 mark  staff  1844050 15 May 10:44 out.jpg

And check the quality used:

identify -format "%Q" out.jpg 
21

Another example:

convert -size 10000x1000 xc:gray +noise random -define jpeg:extent=400kb out.jpg

Result

-rw-r--r--    1 mark  staff   377757 15 May 10:44 out.jpg

And check the quality used:

identify -format "%Q" out.jpg 
5

If you want a way to do something similar with Python, I wrote an answer that works pretty well here. It does a binary search for a JPEG quality that satisfies a maximum size requirement.



Related Topics



Leave a reply



Submit