You have to convert you image back from a numpy array to an image which can then encoded correctly to Base64! What you now do is you encode a numpy array to base64 string which surely can't give the same result the online base64 tool gives!
What you need to do, pass your numpy array to cv2.imencode which returns a buffer image object and then you can convert it to base64
retval, buffer_img= cv2.imencode('.jpg', img)
data = base64.b64encode(buffer_img)
OR
you can skip the img = cv2.imdecode(mydata, -1) and pass mydata directly to base64.b64encode(mydata) while the image is already stored the memory!
There is no openCV image, the openCV image is a ndArray. When you execute print(type(img)) you will get <class 'numpy.ndarray'>
You have to convert you image back from a numpy array to an image which can then encoded correctly to Base64! What you now do is you encode a numpy array to base64 string which surely can't give the same result the online base64 tool gives!
What you need to do, pass your numpy array to cv2.imencode which returns a buffer image object and then you can convert it to base64
retval, buffer_img= cv2.imencode('.jpg', img)
data = base64.b64encode(buffer_img)
OR
you can skip the img = cv2.imdecode(mydata, -1) and pass mydata directly to base64.b64encode(mydata) while the image is already stored the memory!
There is no openCV image, the openCV image is a ndArray. When you execute print(type(img)) you will get <class 'numpy.ndarray'>
The following solved it for me:
import cv2
import base64
# img is a numpy array / opencv image
_, encoded_img = cv2.imencode('.png', img) # Works for '.jpg' as well
base64_img = base64.b64encode(encoded_img).decode("utf-8")
You do not need to save the buffer to a file. The following script captures an image from a webcam, encodes it as a JPG image, and then converts that data into a printable base64 encoding which can be used with your JSON:
import cv2
import base64
cap = cv2.VideoCapture(0)
retval, image = cap.read()
retval, buffer = cv2.imencode('.jpg', image)
jpg_as_text = base64.b64encode(buffer)
print(jpg_as_text)
cap.release()
Giving you something starting like:
/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsICQoKCg
This could be extended to show how to convert it back to binary and then write the data to a test file to show that the conversion was successful:
import cv2
import base64
cap = cv2.VideoCapture(0)
retval, image = cap.read()
cap.release()
# Convert captured image to JPG
retval, buffer = cv2.imencode('.jpg', image)
# Convert to base64 encoding and show start of data
jpg_as_text = base64.b64encode(buffer)
print(jpg_as_text[:80])
# Convert back to binary
jpg_original = base64.b64decode(jpg_as_text)
# Write to a file to show conversion worked
with open('test.jpg', 'wb') as f_output:
f_output.write(jpg_original)
To get the image back as an image buffer (rather than JPG format) try:
jpg_as_np = np.frombuffer(jpg_original, dtype=np.uint8)
image_buffer = cv2.imdecode(jpg_as_np, flags=1)
Some how the above answer doesn't work for me, it needs some update. Here is the new answer to this:
To encode for JSON:
import base64
import json
import cv2
img = cv2.imread('./0.jpg')
string = base64.b64encode(cv2.imencode('.jpg', img)[1]).decode()
dict = {
'img': string
}
with open('./0.json', 'w') as outfile:
json.dump(dict, outfile, ensure_ascii=False, indent=4)
To decode back to np.array:
import base64
import json
import cv2
import numpy as np
response = json.loads(open('./0.json', 'r').read())
string = response['img']
jpg_original = base64.b64decode(string)
jpg_as_np = np.frombuffer(jpg_original, dtype=np.uint8)
img = cv2.imdecode(jpg_as_np, flags=1)
cv2.imwrite('./0.jpg', img)
Hope this could help someone :P
This is my solution for python 3.7 without using PIL.
import base64
def readb64(uri):
encoded_data = uri.split(',')[1]
nparr = np.frombuffer(base64.b64decode(encoded_data), np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
return img
I hope that this solutions works for all.
(Edit: Updated for python 3)
This worked for me on python 3, and doesn't require PIL/pillow or any other dependencies (except cv2):
import cv2
import numpy as np
import base64
def data_uri_to_cv2_img(uri):
encoded_data = uri.split(',')[1]
nparr = np.frombuffer(base64.b64decode(encoded_data), np.uint8)
# old (python 2 version):
# nparr = np.fromstring(encoded_data.decode('base64'), np.uint8)
img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
return img
data_uri = "data:image/jpeg;base64,/9j/4AAQ..."
img = data_uri_to_cv2_img(data_uri)
cv2.imshow("Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
I've been struggling with this issue for a while now and of course, once I post a question - I figure it out.
For my particular use case, I needed to convert the string into a PIL Image to use in another function before converting it to a numpy array to use in OpenCV. You may be thinking, "why convert to RGB?". I added this in because when converting from PIL Image -> Numpy array, OpenCV defaults to BGR for its images.
Anyways, here's my two helper functions which solved my own question:
import io
import cv2
import base64
import numpy as np
from PIL import Image
# Take in base64 string and return PIL image
def stringToImage(base64_string):
imgdata = base64.b64decode(base64_string)
return Image.open(io.BytesIO(imgdata))
# convert PIL Image to an RGB image( technically a numpy array ) that's compatible with opencv
def toRGB(image):
return cv2.cvtColor(np.array(image), cv2.COLOR_BGR2RGB)
Here an example for python 3.6 that uses imageio instead of PIL. It first loads an image and converts it to a b64_string. This string can then be sent around and the image reconstructed as follows:
import base64
import io
import cv2
from imageio import imread
import matplotlib.pyplot as plt
filename = "yourfile.jpg"
with open(filename, "rb") as fid:
data = fid.read()
b64_bytes = base64.b64encode(data)
b64_string = b64_bytes.decode()
# reconstruct image as an numpy array
img = imread(io.BytesIO(base64.b64decode(b64_string)))
# show image
plt.figure()
plt.imshow(img, cmap="gray")
# finally convert RGB image to BGR for opencv
# and save result
cv2_img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
cv2.imwrite("reconstructed.jpg", cv2_img)
plt.show()
You can get a numpy array from you decoded data using:
import numpy as np
...
img = base64.b64decode(msg.payload)
npimg = np.fromstring(img, dtype=np.uint8)
Then you need imdecode to read the image from a buffer in memory. imread is meant to load an image from a file.
So:
import numpy as np
...
def on_message(client, userdata, msg): # msg.payload is incoming data
img = base64.b64decode(msg.payload);
npimg = np.fromstring(img, dtype=np.uint8);
source = cv2.imdecode(npimg, 1)
From the OpenCV documentation we can see that:
imread : Loads an image from a file.
imdecode : Reads an image from a buffer in memory.
Seem a better way to do what you want.
» pip install image-converters