Encoding an opencv image into Base64 doesn't produce a valid image - python-3.x

I am trying to send a picture from my Pi camera to a flask web server. When I encode the image Base64 it doesn't seem to produce a valid image.
I can take the picture and process it through opencv. The Base64 encoded image is passed to the web page, but the string sent is not a valid image. To prove this I have saved the image and processed it with an online Base64 converter. Pasting this string into the web page shows the image.
def Take_Picture(camera):
stream = io.BytesIO() # saving the picture to an in-program stream
camera.resolution = (160,120) # set resolution
camera.capture(stream, format='jpeg', use_video_port=True) # capture into stream
mydata = np.fromstring(stream.getvalue(), dtype=np.uint8) # convert image into numpy array
img = cv2.imdecode(mydata, -1) # to opencv image
data = base64.b64encode(img).decode('utf-8')
print(data)
cv2.imwrite("test.jpg",img)
return data
HTML
<img src="data:image/jpeg;charset=utf-8;base64,{{img}}" alt="Camera View" width="640" height="480">
I get a result of
b'AAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAIAAAMAAAIAAAIAAA...
from data above.
But I get
/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUG...
from test.jpg from an online Base64 conversion. Putting this string in the web page displays the image.

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")

Related

Magick convert through subprocess, Converting tiff images to pdf increases the size by 20 times

I tried using density but it didn't help. The original TIFF image is 459 kB but when it gets converted to PDF the size changes to 8446 KB.
commands = ['magick', 'convert']
commands.extend(waiting_list["images"][2:])
commands.append('-adjoin')
commands.append(combinedFormPathOutput)
process = Popen(commands, stdout=PIPE, stderr=PIPE, shell=True)
process.communicate()
https://drive.google.com/file/d/14V3vKRcyyEx1U23nVC13DDyxGAYOpH-6/view?usp=sharing
Its not teh above code but the below PIL code which is causing the image to increase
images = []
filepath = 'Multi_document_Tiff.tiff'
image = Image.open(filepath)
if filepath.endswith('.tiff'):
imagepath = filepath.replace('.tiff', '.pdf')
for i, page in enumerate(ImageSequence.Iterator(image)):
page = page.convert("RGB")
images.append(page)
if len(images) == 1:
images[0].save(imagepath)
else:
images[0].save(imagepath, save_all=True, append_images=images[1:])
image.close()
When I run
convert Multi_document_Tiff.tiff -adjoin Multi_document.pdf
I get a 473881 bytes PDF that contains the 10 pages of the TIFF. If I run
convert Multi_document_Tiff.tiff Multi_document_Tiff.tiff Multi_document_Tiff.tiff -adjoin Multi_document.pdf
I get a 1420906 bytes PDF that contains 30 pages (three copies of your TIFF).
So obviously if you pass several input files to IM it will coalesce them in the output file.
You code does:
commands.extend(waiting_list["images"][2:])
So it seems it is passing a list of files to IM, and the output should be the accumulation of all these files, which can be a lot bigger that the size of the first file.
So:
did you check the content of the output PDF?
did you check the list of files which is actually passed?

Replace/add Tiff image tags using Pillow from one file to another

I am new to python. I have two tiff images. One with correct tags (source.tif) while the other with incorrect ones (target.tif).
I am able to read the tags of the correct image using the following python script.
from PIL import Image
from PIL.TiffTags import TAGS
Image.MAX_IMAGE_PIXELS = None
# open image
sourceimg = Image.open('images/source.tif')
# extract exif data
exifdata = sourceimg.getexif()
# get dictionary of tags
for tag_id in exifdata:
# get the tag name, instead of human unreadable tag id
tag = TAGS.get(tag_id, tag_id)
data = exifdata.get(tag_id)
# decode bytes
if isinstance(data, bytes):
data = data.decode()
print(f"{tag:25}: {data}")
How can I take these tags from the source and overwrite/add only certain parameters in the target.tif?
The TiffImageFile.tag_v2 dict can be modified and used as the tiffinfo argument when saving the target image:
from PIL import Image
sourceimg = Image.open("source.tif")
targetimg = Image.open("target.tif")
# Get the TIFF tags from the source image
tiffinfo = sourceimg.tag_v2
# Add or modify any tags you want (using the tag number as key)
# Tag number 270 is the ImageDescription tag
tiffinfo[270] = "Example image"
# Save the target image with the correct tags
targetimg.save("target.tif", tiffinfo=tiffinfo)
print(targetimg.tag_v2[270])
>>> Example image

Size of image in bytes, without saving to disk Python

Im trying to get the size of an image that is sent via http request encoded as base64. On the file system the image is a .png image and is around 1,034,023 bytes, however when I receive the image as base64 and get the size in bytes its smaller i.e 840734.
Is this correct and is this due to the compression of .png being different to the image loaded in memory? And if i want he size of the image that is displayed in the file system will I have to re save this image to disk when i receive it?
to get the size of the image in bytes I have the following functions (both return the same value). Im using Python3.
def image_size(imageb64):
character_count = len(imageb64)
padding_count = imageb64[character_count:None].count('=')
count = (3 * (character_count / 4)) - padding_count
print(f'Image size count: {count}')
def image_to_size_in_bytes(numpy_img):
img = Image.fromarray(numpy_img)
buffered = BytesIO()
img.save(buffered, format='PNG')
contents = buffered.getvalue()
print(f'IMAGE SIZE: {len(contents)}')

How to convert image which type is bytes to numpy.ndarray?

I'm trying to optimize my code.
First, I get an image, which type is bytes
Then I have to write that image to file system.
with open('test2.jpg', 'wb') as f:
f.write(content)
Finally I read this image with
from scipy import misc
misc.imread('test2.jpg')
which convert image to np.array.
I want to skip part where I write image to file system, and get np.array.
P.S.: I tried to use np.frombuffer(). It doesn't work for me, cause two np.arrays are not the same.
Convert str to numpy.ndarray
For test you can try yourself:
file = open('test1.jpg', 'rb')
content = file.read()
My first answer in rap...
Wrap that puppy in a BytesIO
And away you go
So, to generate some synthetic data similar to what you get from the API:
file = open('image.jpg','rb')
content = file.read()
That looks like this which has all the hallmarks of a JPEG:
content = b'\xff\xd8\xff\xe0\x00\x10JFIF...
Now for the solution:
from io import BytesIO
from scipy import misc
numpyArray = misc.imread(BytesIO(content))

Python 3 - Base64 Encoding

I have some images, that I need to give to a server using JSON. I decided to use Base64 as encoding system.
In Python 2, I could simply use:
with open(path, "rb") as imageFile:
img_file = imageFile.read()
img_string = base64.b64encode(img_file)
but in Python 3 it doesnt work anymore.
What do I have to change to get this in Python 3 to work?
I followed the solution from this link it seems to work for me. So when you read the image in binary convert it to a string and then just encode the string with base64. The following solution is from the link above. Here is the tested code.
import base64
image = open(image, 'rb')
image_read = image.read()
image_64_encode = base64.encodestring(image_read)
Finally I found a code running on Python 3.7:
# Get the image
image = open(path, 'rb')
image_read = image_read()
# Get the Byte-Version of the image
image_64_encode = base64.b64encode(image_read)
# Convert it to a readable utf-8 code (a String)
image_encoded = image_64_encode.decode('utf-8')
return image_encoded

Resources