Python 3
import base64
from io import BytesIO
buffered = BytesIO()
image.save(buffered, format="JPEG")
img_str = base64.b64encode(buffered.getvalue())
Python 2
import base64
import cStringIO
buffer = cStringIO.StringIO()
image.save(buffer, format="JPEG")
img_str = base64.b64encode(buffer.getvalue())
Answer from Eugene V on Stack Overflowpython - How to convert Image PIL into Base64 without saving - Stack Overflow
python - Faster way to convert a PIL image into base64 - Stack Overflow
Convert image to base64 using python PIL - Stack Overflow
python - Decoding base64 from POST to use in PIL - Stack Overflow
Can I use PIL with Python 2 for image processing?
What is the purpose of Base64 encoding images?
How do I rotate an image in Python using Pillow?
I found the solution. Hope this helps !
img = Image.fromarray(data, 'RGB') #Crée une image à partir de la matrice
buffer = BytesIO()
img.save(buffer,format="JPEG") #Enregistre l'image dans le buffer
myimage = buffer.getvalue()
print "data:image/jpeg;base64,"+base64.b64encode(myimage)
@florian answer helped me a lot but base64.b64encode(img_byte) returned bytes so I needed to decode it to string before concatenation (using python 3.6):
def img_to_base64_str(self, img):
buffered = BytesIO()
img.save(buffered, format="PNG")
buffered.seek(0)
img_byte = buffered.getvalue()
img_str = "data:image/png;base64," + base64.b64encode(img_byte).decode()
As suggested in the comments, I tried pyvips as below:
#!/usr/bin/env python3
import requests
import base64
import numpy as np
from PIL import Image
from io import BytesIO
from cv2 import imencode
import pyvips
def vips_2PNG(image,compression=6):
# Convert PIL Image to Numpy array
na = np.array(image)
height, width, bands = na.shape
# Convert Numpy array to Vips image
dtype_to_format = {
'uint8': 'uchar',
'int8': 'char',
'uint16': 'ushort',
'int16': 'short',
'uint32': 'uint',
'int32': 'int',
'float32': 'float',
'float64': 'double',
'complex64': 'complex',
'complex128': 'dpcomplex',
}
linear = na.reshape(width * height * bands)
vi = pyvips.Image.new_from_memory(linear.data, width, height, bands,dtype_to_format[str(na.dtype)])
# Save to memory buffer as PNG
data = vi.write_to_buffer(f".png[compression={compression}]")
return data
def vips_including_reading_from_disk(image):
# Load image from disk
image = pyvips.Image.new_from_file('stuttgart.png', access='sequential')
# Save to memory buffer as PNG
data = image.write_to_buffer('.png')
return data
def faster(image):
image_arr = np.array(image)
_, byte_data = imencode('.png', image_arr)
return byte_data
def orig(image, faster=True):
output_buffer = BytesIO()
image.save(output_buffer, format='PNG')
byte_data = output_buffer.getvalue()
return byte_data
# img_url = "https://www.cityscapes-dataset.com/wordpress/wp-content/uploads/2015/07/stuttgart03.png"
filename = 'stuttgart.png'
img = Image.open(filename)
# r = orig(img)
# print(len(r))
# %timeit r = orig(img)
# r = faster(img)
# print(len(r))
# %timeit r = faster(img)
# r = vips_including_reading_from_disk(filename)
# print(len(r))
# %timeit r = vips_including_reading_from_disk(filename)
# r = vips_2PNG(img,0)
# print(len(r))
# %timeit r = vips_2PNG(img,0)
I was looking at trading off the compression parameter between file size and speed. Here is what I got - I wouldn't compare absolute values, but rather look at the performance relative to each other on my machine:
Filesize Time
PIL 1.7MB 1.12s
OpenCV 2.0MB 173ms <--- COMPARE
vips(comp=0) 6.2MB 66ms
vips(comp=1) 2.0MB 132ms <--- COMPARE
vips(comp=2) 2.0MB 153ms
I have put arrows next to the ones I would compare.
I use cv2.imencode which is 5x faster than before. Here's the code
import time
import requests
import base64
import numpy as np
from PIL import Image
from io import BytesIO
from cv2 import imencode
# input: single PIL image
def image_to_base64(image, faster=True):
now_time = time.time()
if faster:
image_arr = np.array(image)
_, byte_data = imencode('.png', image_arr)
print('--imencode: ' + str(time.time()-now_time))
else:
output_buffer = BytesIO()
image.save(output_buffer, format='PNG')
byte_data = output_buffer.getvalue()
print('--image.save:' + str(time.time()-now_time))
now_time = time.time()
encoded_input_string = base64.b64encode(byte_data)
print('--base64.b64encode: ' + str(time.time()-now_time))
now_time = time.time()
input_string = encoded_input_string.decode("utf-8")
print('--encoded_input_string.decode: ' + str(time.time()-now_time))
return input_string
img_url = "https://www.cityscapes-dataset.com/wordpress/wp-content/uploads/2015/07/stuttgart03.png"
response = requests.get(img_url)
img = Image.open(BytesIO(response.content))
now_time = time.time()
input_string = image_to_base64(img, faster=True)
print('total: ' + str(time.time()-now_time))
I wonder if there is any solution which can run faster.
Here's a short but complete demo of your code using a ByteIO instead of StringIO. I've also added a function to do the reverse conversion. It runs correctly on Python 2.6 and 3.6. The only difference is that in Python 3 the Base64 output is a b string.
from PIL import Image
from io import BytesIO
import base64
# Convert Image to Base64
def im_2_b64(image):
buff = BytesIO()
image.save(buff, format="JPEG")
img_str = base64.b64encode(buff.getvalue())
return img_str
# Convert Base64 to Image
def b64_2_img(data):
buff = BytesIO(base64.b64decode(data))
return Image.open(buff)
# Test
img = Image.new('RGB', (120, 90), 'red')
img.show()
img_b64 = im_2_b64(img)
print(img_b64)
new_img = b64_2_img(img_b64)
new_img.show()
Python 3 output
b'/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCABaAHgDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDi6KKK+ZP3EKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigAooooAKKKKACiiigD/9k='
You can use this function to convert an image to base64 string.
import base64
def image_to_base64(image_path):
with open(image_path, "rb") as image_file:
encoded_string = base64.b64encode(image_file.read()).decode("utf-8")
return encoded_string
base64String = image_to_base64('image.jpg')
You should try something like:
from PIL import Image
from io import BytesIO
import base64
data['img'] = '''R0lGODlhDwAPAKECAAAAzMzM/////wAAACwAAAAADwAPAAACIISPeQHsrZ5ModrLl
N48CXF8m2iQ3YmmKqVlRtW4MLwWACH+H09wdGltaXplZCBieSBVbGVhZCBTbWFydFNhdmVyIQAAOw=='''
im = Image.open(BytesIO(base64.b64decode(data['img'])))
Your data['img'] string should not include the HTML tags or the parameters data:image/jpeg;base64 that are in the example JSFiddle.
I've changed the image string for an example I took from Google, just for readability purposes.
There is a metadata prefix of data:image/jpeg;base64, being included in the img field. Normally this metadata is used in a CSS or HTML data URI when embedding image data into the document or stylesheet. It is there to provide the MIME type and encoding of the embedded data to the rendering browser.
You can strip off the prefix before the base64 decode and this should result in valid image data that PIL can load (see below), but you really need to question how the metadata is being submitted to your server as normally it should not.
import re
import cStringIO
from PIL import Image
image_data = re.sub('^data:image/.+;base64,', '', data['img']).decode('base64')
image = Image.open(cStringIO.StringIO(image_data))
You need to base64 encode before you can decode.
You can achieve this without creating a temporary file by using an in memory file, with io.BytesIO()
in_mem_file = io.BytesIO()
img.save(in_mem_file, format = "PNG")
img_bytes = in_mem_file.getvalue()
base64_encoded_result_bytes = base64.b64encode(img_bytes)
# b64encode returns a byte string so needs converting back to a string
base64_encoded_result_str = base64_encoded_result_bytes.decode('ascii')
Image.frombytes() does not create an image from a base64 encoded string, see documentation.
If you want to reverse the encoding, use:
img2 = base64.b64decode(base)