19

I'd like to work directly with compressed JPEG images. I know that with PIL/Pillow I can compress an image when I save it, and then read back the compressed image - e.g.

from PIL import Image
im1 = Image.open(IMAGE_FILE)
IMAGE_10 = os.path.join('./images/dog10.jpeg')
im1.save(IMAGE_10,"JPEG", quality=10)
im10 = Image.open(IMAGE_10)

but, I'd like a way to do this without the extraneous write and read. Is there some Python package with a function that will take an image and quality number as inputs and return a jpeg version of that image with the given quality?

user1245262
  • 6,968
  • 8
  • 50
  • 77

2 Answers2

19

For in-memory file-like stuff, you can use StringIO. Take a look:

from io import StringIO # "import StringIO" directly in python2
from PIL import Image
im1 = Image.open(IMAGE_FILE)

# here, we create an empty string buffer    
buffer = StringIO.StringIO()
im1.save(buffer, "JPEG", quality=10)

# ... do something else ...

# write the buffer to a file to make sure it worked
with open("./photo-quality10.jpg", "w") as handle:
    handle.write(buffer.contents())

If you check the photo-quality10.jpg file, it should be the same image, but with 10% quality as the JPEG compression setting.

M.T
  • 4,917
  • 4
  • 33
  • 52
Sam Gammon
  • 1,457
  • 10
  • 16
  • 6
    Doesn't work for me with Python 3.5. I changed `buffer = StringIO.String()` to `buffer = StringIO()` and imported `from io import StringIO`, but when I try to do `im1.save()` it crashes with `TypeError: string argument expected, got 'bytes'` – Groosha Nov 16 '16 at 16:39
  • 1
    I think it's supposed to be: `buffer=StringIO.StringIO()` – Mark Walker Jun 11 '17 at 06:15
  • 1
    Now you have to use BytesIO insted of StringIO eg `from io import BytesIO` ; `buffer = BytesIO()` – Andrea993 Oct 28 '20 at 11:26
  • @Andrea993 I followed your update but got this error: `handle.write(buffer.contents()) … AttributeError: '_io.BytesIO' object has no attribute 'contents'` – Madamadam Sep 09 '21 at 22:40
  • I had to change: `with open("./photo-quality10.jpg", "wb") as handle: handle.write(buffer.getbuffer())` – DavidBu Sep 27 '21 at 15:53
11

Using BytesIO

try:
    from cStringIO import StringIO as BytesIO
except ImportError:
    from io import BytesIO

def generate(self, image, format='jpeg'):
    im = self.generate_image(image)
    out = BytesIO()
    im.save(out, format=format,quality=75)
    out.seek(0)
    return out

StringIO is missing in Python3.0, ref to : StringIO in python3

Community
  • 1
  • 1
Super Engine
  • 111
  • 1
  • 2