I believe since numpy.arrays support the buffer protocol, you just need the following:
processed_string = base64.b64encode(img)
So, for example:
>>> encoded = b"aGVsbG8sIHdvcmxk"
>>> img = np.frombuffer(base64.b64decode(encoded), np.uint8)
>>> img
array([104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100], dtype=uint8)
>>> img.tobytes()
b'hello, world'
>>> base64.b64encode(img)
b'aGVsbG8sIHdvcmxk'
>>>
Answer from juanpa.arrivillaga on Stack OverflowI believe since numpy.arrays support the buffer protocol, you just need the following:
processed_string = base64.b64encode(img)
So, for example:
>>> encoded = b"aGVsbG8sIHdvcmxk"
>>> img = np.frombuffer(base64.b64decode(encoded), np.uint8)
>>> img
array([104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100], dtype=uint8)
>>> img.tobytes()
b'hello, world'
>>> base64.b64encode(img)
b'aGVsbG8sIHdvcmxk'
>>>
I have the same problem. After some search and try, my final solution is almost the same as yours.
The only difference is that the base64 encoded string is png format data, so I need to change it from RGBA to RGB channels before converted to np.array:
image = image.convert ("RGB")
img = np.array(image)
In the reverse process, you treate the data as JPEG format, maybe this is the reason why new_image_string is not identical to base64_image_string ?
How to convert numpy image array (output from a DL model) to base64 and sent to front end html
Black b64-encoded image from numpy array
python - Convert numpy array to base64 - Stack Overflow
python - Converting to base64 changes numpy array - Stack Overflow
Here's an example to show what you need to do:
from PIL import Image
import io
import base64
import numpy
# creare a random numpy array of RGB values, 0-255
arr = 255 * numpy.random.rand(20, 20, 3)
im = Image.fromarray(arr.astype("uint8"))
#im.show() # uncomment to look at the image
rawBytes = io.BytesIO()
im.save(rawBytes, "PNG")
rawBytes.seek(0) # return to the start of the file
print(base64.b64encode(rawBytes.read()))
I can paste the string printed into the base64 image converter and it'll look similar to im.show(), as the site enlarges the image.
You may need to manipulate your array or provide an appropriate PIL mode when creating your image
This is a bit more round-about from the other answers, but in case someone else lands here trying to show an image as a Qt base-64 tooltip, the other methods won't work. I had more luck using a QBuffer and QImage:
# Adapted from https://stackoverflow.com/a/34836998/9463643
import qimage2ndarray as q2n
buffer = QtCore.QBuffer()
buffer.open(buffer.WriteOnly)
buffer.seek(0)
img = (np.random.random((100,100))*255).astype('uint8')
img = q2n.array2qimage(img)
img.save(buffer, "PNG", quality=100)
encoded = bytes(buffer.data().toBase64()).decode() # <-- Here's the base 64 image
html = f'<img src="data:image/png;base64,{encoded}">'
element.setToolTip(html)
Fuys, I desperately need help.
I have a model loaded serverside which takes in images and gives a numpy array output of size (n,240,320,3), where n is the number of output images.
I want to convert these individual images to base64 strings, append it to a list and render_template a fron end html page with the list as data passed to it, so that i can put it in image tags.
im trying to do this with open cv, the following is the method im using:
'''
currimg = outputs[i,:,:,:] / 255
retval, buffer = cv2.imencode('.png',currimg)
pstr = base64.b64encode(buffer)
pstr = pstr.decode('utf-8')
proc64.append(pstr)
'''
when i do this normally in another python script, it works fine and gives me a proper base64 output.
but for some reason when i do it over the flask server, it always outputs some wrong base64 string (probably incomplete) and always gives a black image as output.
Any help would greatly be appreciated. Thanks in advance!
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.