I wrote this code :
with Image.open(objective.picture.read()) as image:
image_file = BytesIO()
exifdata = image.info['exif']
image.save(image_file, 'JPEG', quality=50, exif=exifdata)
zf.writestr(zipped_filename, image_file)
Which is supposed to open the image stored in my model (this is in a Django application). I want to reduce the quality of the image file before adding it to the zipfile (zf). So I decided to work with BytesIO to prevent writing useless file on the disk. Though I'm getting an error here. It says :
embedded NUL character
Could someone help me out with this ? I don't understand what's going on.
Well I was kind of dumb. objective.picture.read() returns a byte string (really long byte string...) so I shouldn't have used Image but ImageFile.Parser() and feed that byte string to the parser so it can return an Image that I can work with. Here is the code :
from PIL import ImageFile
from io import BytesIO
p = ImageFile.Parser()
p.feed(objective.picture.read())
image = p.close()
image_file = BytesIO()
exifdata = image.info['exif']
image.save(image_file, 'JPEG', quality=50, exif=exifdata)
# Here zf is a zipfile writer
zf.writestr(zipped_filename, image_file.getvalue())
The close() actually returns the image parsed from the bytestring.
Here is the doc : The ImageFile Documentation
Related
I am using image compression to reduce the image size. When submitting the post request, I am not getting any error, but can't figure out why the images do not get saved. Here is my code:
#app.post("/post_ads")
async def create_upload_files(title: str = Form(),body: str = Form(),
db: Session = Depends(get_db), files: list[UploadFile] = File(description="Multiple files as UploadFile")):
for file in files:
im = Image.open(file.file)
im = im.convert("RGB")
im_io = BytesIO()
im = im.save(im_io, 'JPEG', quality=50)
PIL.Image.open() takes as fp argumnet the following:
fp – A filename (string), pathlib.Path object or a file object. The
file object must implement file.read(), file.seek(), and
file.tell() methods, and be opened in binary mode.
Using a BytesIO stream, you would need to have something like the below (as shown in client side of this answer):
Image.open(io.BytesIO(file.file.read()))
However, you don't really have to use an in-memory bytes buffer, as you can get the the actual file object using the .file attribute of UploadFile. As per the documentation:
file: A SpooledTemporaryFile (a file-like object).
This is the actual Python file that you can pass directly to other
functions or libraries that expect a "file-like" object.
Example - Saving image to disk:
# ...
from fastapi import HTTPException
from PIL import Image
#app.post("/upload")
def upload(file: UploadFile = File()):
try:
im = Image.open(file.file)
if im.mode in ("RGBA", "P"):
im = im.convert("RGB")
im.save('out.jpg', 'JPEG', quality=50)
except Exception:
raise HTTPException(status_code=500, detail='Something went wrong')
finally:
file.file.close()
im.close()
Example - Saving image to an in-memory bytes buffer (see this answer):
# ...
from fastapi import HTTPException
from PIL import Image
#app.post("/upload")
def upload(file: UploadFile = File()):
try:
im = Image.open(file.file)
if im.mode in ("RGBA", "P"):
im = im.convert("RGB")
buf = io.BytesIO()
im.save(buf, 'JPEG', quality=50)
# to get the entire bytes of the buffer use:
contents = buf.getvalue()
# or, to read from `buf` (which is a file-like object), call this first:
buf.seek(0) # to rewind the cursor to the start of the buffer
except Exception:
raise HTTPException(status_code=500, detail='Something went wrong')
finally:
file.file.close()
buf.close()
im.close()
For more details and code examples on how to upload files/images using FastAPI, please have a look at this answer and this answer. Also, please have a look at this answer for more information on defining your endpoint with def or async def.
I assume you are writing to a BytesIO to get an "in memory" JPEG without slowing yourself down by writing to disk and cluttering your filesystem.
If so, you want:
from PIL import Image
from io import BytesIO
im = Image.open(file.file)
im = im.convert("RGB")
im_io = BytesIO()
# create in-memory JPEG in RAM (not disk)
im.save(im_io, 'JPEG', quality=50)
# get the JPEG image in a variable called JPEG
JPEG = im_io.get_value()
I'm trying to open a PNG file with Python. I do believe I have a properly encoded PNG.
It starts with:
\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR
And ends with:
\x00IEND\xaeB`\x82
My code so far:
import PIL.Image as Image
with open('./test_image_3.txt', 'rb') as f:
b = f.read()
b = base64.b64decode(b).decode("unicode_escape").encode("latin-1")
b = b.decode('utf-16-le')
img = Image.open(io.BytesIO(b))
img.show()
b = base64.b64decode(b).decode("unicode_escape").encode("latin-1")
UnicodeDecodeError: 'unicodeescape' codec can't decode bytes in position 178-179: truncated \uXXXX escape
Unfortunately I can't read the file you've provided as the website butchered it massively. Either use pastebin or github (or something similar) where it'll be possible to retrieve text/plain e.g. via curl so I can attempt to reproduce the problem 1:1 for the contents.
However, the general approach would be this:
from PIL import Image
with Image.open("./test_image_3.txt") as im:
im.show()
it's directly from Pillow's documentation and it does not care about the file's name or extension.
Alternatively, if you have open() call with a file handle:
from PIL import Image
with open("./test_image_3.txt", "rb") as file:
with Image.open(file) as im:
im.show()
And if you have it mangled somehow, then judging from your encode() and decode() calls it would be this:
from PIL import Image
from io import BytesIO
data = <some raw PNG bytes, the original image>
# here I store it in that weird format and write as bytes
with open("img.txt", "wb") as file:
file.write(data.decode("latin-1").encode("unicode_escape"))
# here I read it back as bytes, reverse the chain of calls and invert
# the call pairs for en/decoding so encode() -> decode() and vice-versa
with open("img.txt","rb") as file:
content = BytesIO()
content.write(
file.read().decode("unicode_escape").encode("latin-1")
)
# seek back, so the BytesIO() can return back the full content
content.seek(0)
# then simply read as if using a file handle
with Image.open(content) as img:
img.show()
The main problem is that, from what I could find, there is no easy/documented way to load an image from base64 encoded image. I use the following code to encode the image to base64 (so that I wouldn't need to include all the images with the source, nor should I create temp files and delete them at exit). The image format I use is .png which is supported in Gtk3+. (from GdkPixbuf.Pixbuf.get_formats() i have ['png'] in the results. I am really confused on how to use Gtk3+ for this purpose.
import base64
image_name = 'image.png'
image_loc = 'd:\\Home\\' + image_name
with open(image_loc, 'rb') as image_file:
encoded_string = base64.b64encode(image_file.read())
print(encoded_string)
I want to use the output for example:
base64_data="""
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAABsklEQVRYhe2XIVMCQRSAv2AwGAgEAuFmJBiMFmcMBH8AgUAgXHDMFww2gtFA
IBicsRgNBgLBcS7yE4gEIsFgIBDO8Pa8x7FwB7cnxTfzyr637327b+/dLiTSBIbAHIgydAGMgAscyUOOpDZdAu2iyZsq4BcwAHpb9NE1xFAl
P8s558klRFzzwQ5zejgsRxygVxBgbwiXAHtBuAaIIa7KBAgyACJgBlTKAqgBH8A0pWmIXKXYB2CbdFRM/xAA3qEBKipm8A9wCIAa8q/oUOJn
6FTKAqgA10gZWkD9rwAugRfWm1IEfCKlKQ2ghdwrstp0vwyAuiX5HGnRMwtE1zVAfLPS6hubZ7HNgaorgFPkppxOEvcBG0AE3LoCuGZ1Zb7R
hrGfqLGJ8h24ArhTcaYZvqHyDV0BtFWcGbLlHrJygCM1Nla+r5Cc0OcCAA3sNfaN3dtgDwDeSO5xzQIQthvRNoAlcA7yGFmowTFSmzz6jmwv
rL6wYp0Yv7HFttKMusC3xSmP3qs4/ZxzJiTn41c85N032mEHQqQBHacWs+mFvTSQa8ldSxW4Qb7zEDntAabmWn4A0clKl9nNvDwAAAAASUVO
RK5CYII
"""
And render the image from base64.
As a side note, on tkinter this was easily done with:
tkinter.PhotoImage(data=base64_data)
And then display the image where you needed it.
Getting back to Gtk3+, I didn't find a method of loading the image from base64. Even with GdkPixbuf.Pixbuf.new_from_data, I get a broken image. I have also tried with Gio.MemoryInputStream.new_from_bytes, but it says that the format of the image isn't supported.
Your data is base64 encoded, in order for Gtk3+ to use it, you must first decode it:
import base64
raw_data = base64.b64decode(data)
Then you were right with GdkPixbuf.Pixbuf.new_from_data:
(I cannot test, but I think this may work)
import base64
raw_data = base64.b64decode(data)
image = GdkPixbuf.Pixbuf.new_from_data(raw_data)
image_show_2.set_from_pixbuf(image)
Else you can do as you showed:
import base64
raw_data = base64.b64decode(data)
byting = GLib.Bytes(raw_data)
inputing = Gio.MemoryInputStream.new_from_bytes(byting)
image = GdkPixbuf.Pixbuf.new_from_data(inputing)
image_show_2.set_from_pixbuf(image)
I am writing an application that uses images intensively. It is composed of two parts. The client part is written in Python. It does some preprocessing on images and sends them over TCP to a Node.js server.
After preprocessing, the Image object looks like this:
window = img.crop((x,y,width+x,height+y))
window = window.resize((48,48),Image.ANTIALIAS)
To send that over socket, I have to have it in binary format. What I am doing now is:
window.save("window.jpg")
infile = open("window.jpg","rb")
encodedWindow = base64.b64encode(infile.read())
#Then send encodedWindow
This is a huge overhead, though, since I am saving the image to the hard disk first, then loading it again to obtain the binary format. This is causing my application to be extremely slow.
I read the documentation of PIL Image, but found nothing useful there.
According to the documentation, (at effbot.org):
"You can use a file object instead of a filename. In this case, you must always specify the format. The file object must implement the seek, tell, and write methods, and be opened in binary mode."
This means you can pass a StringIO object. Write to it and get the size without ever hitting the disk.
Like this:
s = StringIO.StringIO()
window.save(s, "jpg")
encodedWindow = base64.b64encode(s.getvalue())
use BytesIO
from io import BytesIO
from PIL import Image
photo=Image.open('photo.jpg')
s=BytesIO()
photo.save(s,'jpeg')
data = s.getvalue()
with open('photo2.jpg', mode='wb') as f:
f.write(data)
It's about the difference between in-memory file-like object and BufferedReader object.
Here is my experiment in Jupyter(Python 3.8.10):
from PIL import Image as PILImage, ImageOps as PILImageOps
from IPython.display import display, Image
from io import BytesIO
import base64
url = "https://learn.microsoft.com/en-us/archive/msdn-magazine/2018/april/images/mt846470.0418_mccaffreytrun_figure2_hires(en-us,msdn.10).png"
print("get computer-readable bytes from the url")
img_bytes = requests.get(url).content
print(type(img_bytes))
display(Image(img_bytes))
print("convert to in-memory file-like object")
in_memory_file_like_object = BytesIO(img_bytes)
print(type(in_memory_file_like_object))
print("convert to an PIL Image object for manipulating")
pil_img = PILImage.open(in_memory_file_like_object)
print("let's rotate it, and it remains a PIL Image object")
pil_img.show()
rotated_img = pil_img.rotate(45)
print(type(rotated_img))
print("let's create an in-memory file-like object and save the PIL Image object into it")
in_memory_file_like_object = BytesIO()
rotated_img.save(in_memory_file_like_object, 'png')
print(type(in_memory_file_like_object))
print("get computer-readable bytes")
img_bytes = in_memory_file_like_object.getvalue()
print(type(img_bytes))
display(Image(img_bytes))
print('convert to base64 to be transmitted over channels that do not preserve all 8-bits of data, such as email')
# https://stackoverflow.com/a/8909233/3552975
base_64 = base64.b64encode(img_bytes)
print(type(base_64))
# https://stackoverflow.com/a/45928164/3552975
assert base64.b64encode(base64.b64decode(base_64)) == base_64
In short you can save a PIL Image object into an in-memory file-like object by rotated_img.save(in_memory_file_like_object, 'png') as shown above, and then conver the in-memory file-like object into base64.
from io import BytesIO
b = BytesIO()
img.save(b, format="png")
b.seek(0)
data = b.read()
del b
I am looking to create base64 inline encoded data of images for display in a table using canvases. Python generates and creates the web page dynamically. As it stands python uses the Image module to create thumbnails. After all of the thumbnails are created Python then generates base64 data of each thumbnail and puts the b64 data into hidden spans on the user's webpage. A user then clicks check marks by each thumbnail relative to their interest. They then create a pdf file containing their selected images by clicking a generate pdf button. The JavaScript using jsPDF generates the hidden span b64 data to create the image files in the pdf file and then ultimately the pdf file.
I am looking to hopefully shave down Python script execution time and minimize some disk I/O operations by generating the base64 thumbnail data in memory while the script executes.
Here is an example of what I would like to accomplish.
import os, sys
import Image
size = 128, 128
im = Image.open("/original/image/1.jpeg")
im.thumbnail(size)
thumb = base64.b64encode(im)
This doesn't work sadly, get a TypeErorr -
TypeError: must be string or buffer, not instance
Any thoughts on how to accomplish this?
You first need to save the image again in JPEG format; using the im.tostring() method would otherwise return raw image data that no browser would recognize:
from io import BytesIO
output = BytesIO()
im.save(output, format='JPEG')
im_data = output.getvalue()
This you can then encode to base64:
image_data = base64.b64encode(im_data)
if not isinstance(image_data, str):
# Python 3, decode from bytes to string
image_data = image_data.decode()
data_url = 'data:image/jpg;base64,' + image_data
Here is one I made with this method:
data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCACAAIADASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwD3+iiigAooooAKwde8Y6D4aQ/2nqEccuNwhU7pG/4CKx/iX4zfwd4eEtqEN/cv5cAb+Hj5nx3xx+JFfMF3fXN/dSXN1M808rbnkc5LE0mwPeNU+PGkwoRpmm3NxJ6zkRr+mTWK/wAfb8x4TRLYP6mViPyryq10XVL5A9rp11Mn96OFmH5gVfj8H+IWOP7GvR/2xap5l3HZnpNt8fbsSD7VokDJ38qYqf1Brp9D+Nfh7UW2ajHLpshOAW/eIf8AgQHH5V4o3gnxGASNGvDj0iNUbjSL6y/4+rO4g/66xMv86FJPZhys+u7HUbLU7fz7G7guYTxvhkDj8xVuvjzS9b1Tw9fLc6Zey28gI+43DezDuPY19QeB/EbeKfClpqciqs7ZSZU6B1OD+fX8apMR0dFFFMAooooAKKKKACiiigD5u+NuqzXvjo2DcQ2MKIg93UMT+o/Kt/wJ4As7LT4NR1O3Sa+lUSKkgysQ7cetYPju2Gq/G97OQBkea3Rgf7uxC36ZrvPGmsz6F4amubQhZiRGjldwTPfFcmIk9IR6m9KK1kzpkjQD5AMDtUoTHSvIPhl4s1XVNYS1ubme6DKzz+ZGAseD8u1h1/KvZVHNcc4OErM6Iy5ldDVHA45oeOCVTHKiujcFWXIP4VMi5GO9fPuqeLvENv4y1BXm1EXCShbaFMLEp3DKsuDuXGR2PQ1VODnsKU+U6H4meAbexs21rSIRHCp/0iFBwuf4l9B6itX4B30pXWLAsTEvlzKPRjkH+Q/Ku8vkN54YuUnjGZbVg6H1KmvNfgJME1vV4CeXt1cD/dbH/s1dmHm5KzOetFJ3R7zRRRXSYhRRRQAUUVyXi74iaH4PxDdyNPesu5baHBYD1b+6KAOtorxf/hoG36f8I/J/4FD/AOJqpffH6ZomWz0FUc9GluNwH4BRSA5bxNr0Fn8a7vVJRvgt7wI+OcBVCE/hj9K9aivdH1mwIFxZ3dpIvILqwI9xXzPcXE11dzXM775pnLux/iYnJqLNYVaPtNTSnU5D6dsU8PaOjCzfT7Td94q6gn61cbxFosX+s1iwX63Cf418sq3NSbiO9ZfVe7NPb+R9RJ4q8P4z/benf+BKf41HJrHhGa6S7lv9Ge4X7srSxlx+PWvmRJOeSalEhC9x9Kf1Vdxe28j3fxh8SdEsdHubXTrlLy9ljaNPK5RMjG4t049q8/8AgxqP2T4hW8RbC3UMkJ/LcP1UVw5y55yTU+j6nPoWt2ep265ltpVkUHocdj9a2p01TWhlObkfZVFeU2Hx30CaIfbrC+tpO4QLIv55B/Srcnxy8KoQEh1GT3EKj+bVqSel0Vg+G/GGi+KoGl0u63sn34nG10+o/rW9TArX9yLPT7m6K7hDE0mPXAzXxrqup3OralcX95KZLidy7sfWvrjxe0y+DtZNum+YWU21f+AGvjl+DSAA2DS76jzSHjvSAl3CkJqEHNPzQBID83pUvT3quDzT1bNAEoJGKeDUOcUoegCyHx7+opGcE8jn1qv5mO5ppk5wKALW1OTnA9KcCmKo7yKkWT6UAdD4W12Xw54lstSjJxDKN6A43IfvL+VfXKOJI1deQwyK+Ko23MtfZumKV0qzUnJECAn1+UUwLLKGUqQCDxg18w/Er4c33hnVJ760gMukzuWjeNSfJzzsb09jX1BSEBhgjIpgfDeDyKawNfap0HRjKZDpNiXPVvs6ZP44p8ui6VNEY5dMs3Q/wtApH8qVgPicCn4x1rqfiHokPh/x1qlhbIEgWQPEg6KrKGC/hux+FcuaQhueeKUdabRmgCdenpTgKiVvenhs0DApimgU8/N3rd8IaOmueKtN02XPlTzqsmOPl6t+gNAHPeW7thVJ9gKmWzuCceS+RzjbX2Xp+iaXpMCQ2On21tGnQRRAVeEaA5CDPrinYD5S8EeB9R8VaxFCsMkVmrZuLgr8qr6A+voK+rY4xFEka8KoAFOAA6DFLTAKKKKACiiigD5l+N8bL8Q5mOMNbxFcemMf0rzVq9J+Nu//AIWJcb8bfs8W3Hpt/wAc15sxqRDDzSCn96aRg0AKKeDUfHSnA+tAEymuu+Gz7PiForetwB+YIrkB9a634cjd4+0b/r5WgZ9Z0Ug6UtUAUUUUAMSRHGVYGnVwVvq1wmMOavJ4guFH3qm5XKdhSEgCuRPiWUdWFV5/EUjr980XDlPHfjfKJPH74TaBbRjP97rz/T8K80Ndv8UbtrzxZvZs7YEX+Z/rXEdaCRMZ5FNbg89DUgrs/hl4OtfGfio2d+zizghM8qocF8EALnt1/SgRwwb5sU8V6D8YvBemeEfEFgdIiMNpdwFvJLltrKcHBJzzkfrXn69KAHqea7r4VQ+b8Q9JXHR2bn2RjXDL1r0L4PIG+I2nn+6kp/8AIbUDPp+iiiqAKKKKAMCPw3EB8zUTeHUKnYwJrfozSsPmZ59qGh3cGWWI7fWsGXfCSH4r11lVlIIBB7Vi6l4YstQU8eWx7ilYdz5p+IwB1yFxg7rden+81cbwa988ZfCXU9ViWSxlhlmizsBbbuB7HNefN8H/ABuD/wAgXP8A28xf/FUEs4Va9C+D+qHSfGhkIzFJbsknsMqf6UkPwZ8aSMA2mxRj1a5j/oa9M8FfCNNI0yZtVn/0+fGfJOVjUds96AOF+O+pR6nrmkmFt0Mdu+D7luf6V5Wte8eLPhFq+ooBaTxXAQ5jLNtYexz/AI1wFx8I/GluxA0dpR6xyof/AGagDiV613Hwr83/AIWNpHlHne2f93Y2f0qsnww8ZF9v9g3WffaB+ea9i+GHwzl8LSNq2qsrag6bI4l5EIPXnue350wPUKKKKYBRRRQB/9k=
Unfortunately the Markdown parser doesn't let me use this as an actual image, but you can see it in action in a snippet instead:
<img src="data:image/jpg;base64,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"/>
In Python 3, you may need to use BytesIO:
from io import BytesIO
...
outputBuffer = BytesIO()
bg.save(outputBuffer, format='JPEG')
bgBase64Data = outputBuffer.getvalue()
# http://stackoverflow.com/q/16748083/2603230
return 'data:image/jpeg;base64,' + base64.b64encode(bgBase64Data).decode()
thumb = base64.b64encode(im.tostring())
I think would work
I use PNG when I save to the buffer. With JPEG the numpy arrays are a bit different.
import base64
import io
import numpy as np
from PIL import Image
image_path = 'dog.jpg'
img2 = np.array(Image.open(image_path))
# Numpy -> b64
buffered = io.BytesIO()
Image.fromarray(img2).save(buffered, format="PNG")
b64image = base64.b64encode(buffered.getvalue())
# b64 -> Numpy
img = np.array(Image.open(io.BytesIO(base64.b64decode(b64image))))
print(img.shape)
np.testing.assert_almost_equal(img, img2)
Note that it will be slower.