7

I am writing an application that uses images intensively. It is composed of two parts. The client part is written in Python. It does some preprocessing on images and sends them over TCP to a Node.js server. After preprocessing, the Image object looks like this:

window = img.crop((x,y,width+x,height+y))
window = window.resize((48,48),Image.ANTIALIAS)

To send that over socket, I have to have it in binary format. What I am doing now is:

window.save("window.jpg")
infile = open("window.jpg","rb")
encodedWindow = base64.b64encode(infile.read())
#Then send encodedWindow 

This is a huge overhead, though, since I am saving the image to the hard disk first, then loading it again to obtain the binary format. This is causing my application to be extremely slow. I read the documentation of PIL Image, but found nothing useful there.

JackOrJones
  • 304
  • 1
  • 6
  • 15

4 Answers4

4

According to the documentation, (at effbot.org):

"You can use a file object instead of a filename. In this case, you must always specify the format. The file object must implement the seek, tell, and write methods, and be opened in binary mode."

This means you can pass a StringIO object. Write to it and get the size without ever hitting the disk.

Like this:

s = StringIO.StringIO()
window.save(s, "jpg")
encodedWindow = base64.b64encode(s.getvalue())
swstephe
  • 1,840
  • 11
  • 17
  • That gives the following exception: File "/usr/local/lib/python2.7/dist-packages/PIL/Image.py", line 1670, in save raise KeyError(ext) # unknown extension – JackOrJones Dec 26 '14 at 02:10
  • @Wahbivic use `window.save(s, "jpg")`. With PIL/Pillow, if it can't detect the format by the filename (or if there is no filename, like here), you need to specify the format you want. – MattDMo Dec 26 '14 at 02:14
  • @MattDMo Thanks a million. For some weird reason, window.save(s, "jpg") raised an exception as well. window.save(s,"jpeg") worked fine, though. – JackOrJones Dec 26 '14 at 02:19
  • @Wahbivic no prob. What version of PIL/Pillow are you using? I keep Pillow up to date, and `"jpg"` has never been an issue for me. Are you still using the ancient 1.1.7 version of PIL? – MattDMo Dec 26 '14 at 02:21
  • @swstephe Thanks a million, as well. I edited your answer to what actually worked. – JackOrJones Dec 26 '14 at 02:22
  • @MattDMo Yeah, that's the reason. I thought that I updated it some time in the past, but apparently it is still 1.1.7 – JackOrJones Dec 26 '14 at 02:26
  • @Wahbivic check out https://pillow.readthedocs.org for the latest docs. `pip install pillow` should work just fine if you have the required libraries installed. – MattDMo Dec 26 '14 at 02:31
  • @swstephe I was actually using import Image not from PIL import Image, so that's the reason. PIL is up to date. – JackOrJones Dec 26 '14 at 02:54
0

use BytesIO

from io import BytesIO
from PIL import Image

photo=Image.open('photo.jpg')

s=BytesIO()
photo.save(s,'jpeg')

data = s.getvalue()

with open('photo2.jpg', mode='wb') as f:
    f.write(data)
Kaan
  • 23
  • 4
-1

It's about the difference between in-memory file-like object and BufferedReader object.

Here is my experiment in Jupyter(Python 3.8.10):

from PIL import Image as PILImage, ImageOps as PILImageOps
from IPython.display import display, Image
from io import BytesIO
import base64

url = "https://learn.microsoft.com/en-us/archive/msdn-magazine/2018/april/images/mt846470.0418_mccaffreytrun_figure2_hires(en-us,msdn.10).png"
print("get computer-readable bytes from the url")
img_bytes = requests.get(url).content
print(type(img_bytes))
display(Image(img_bytes))
print("convert to in-memory file-like object")
in_memory_file_like_object = BytesIO(img_bytes)
print(type(in_memory_file_like_object))

print("convert to an PIL Image object for manipulating") 
pil_img = PILImage.open(in_memory_file_like_object)
print("let's rotate it, and it remains a PIL Image object") 
pil_img.show()
rotated_img = pil_img.rotate(45)
print(type(rotated_img))
print("let's create an in-memory file-like object and save the PIL Image object into it")  
in_memory_file_like_object = BytesIO()
rotated_img.save(in_memory_file_like_object, 'png')
print(type(in_memory_file_like_object))

print("get computer-readable bytes") 
img_bytes = in_memory_file_like_object.getvalue()
print(type(img_bytes))
display(Image(img_bytes))
print('convert to base64 to be transmitted over channels that do not preserve all 8-bits of data, such as email')
# https://stackoverflow.com/a/8909233/3552975
base_64 = base64.b64encode(img_bytes)
print(type(base_64))
# https://stackoverflow.com/a/45928164/3552975
assert base64.b64encode(base64.b64decode(base_64)) == base_64

In short you can save a PIL Image object into an in-memory file-like object by rotated_img.save(in_memory_file_like_object, 'png') as shown above, and then conver the in-memory file-like object into base64.

Lerner Zhang
  • 6,184
  • 2
  • 49
  • 66
-2
from io import BytesIO

b = BytesIO()
img.save(b, format="png")
b.seek(0)
data = b.read()
del b
SzieberthAdam
  • 3,999
  • 2
  • 23
  • 31