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 - Faster way to convert a PIL image into base64 - Stack Overflow
python - How to convert Image PIL into Base64 without saving - Stack Overflow
Convert image to base64 using python PIL - Stack Overflow
python - How to get a PIL image as a Base64 encoded string - 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?
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.
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()
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 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)