Load image from string - python

Given a string containing jpeg image data, is it possible to load this directly in pygame?
I've tried using StringIO but failed and I don't completely understand the 'file-like' object concept.
Currently, as a workaround, I'm saving to disk and then loading an image the standard way:
# imagestring contains a jpeg
f=open('test.jpg','wb')
f.write(imagestring)
f.close()
image=pygame.image.load('test.jpg')
Any suggestions on improving this so that we avoid creating a temp file?

fstr = cStringIO.StringIO(simage)
pygame.image.load(fstr, namehint="somethinguseful")

Related

Save JPEG comment using Pillow

I need to save an Image in Python (created as a Numpy array) as a JPEG file, while including a "comment" in the file with some specific metadata. This metadata will be used by another (third-party) application and is a simple ASCII string. I have a sample image including such a "comment", which I can read out using Pillow (PIL), via the image.info['comment'] or the image.app['COM'] property. However, when I try a simple round-trip, i.e. loading my sample image and save it again using a different file name, the comment is no longer preserved. Equally, I found no way to include a comment in a newly created image.
I am aware that EXIF tags are the preferred way to save metadata in JPEG images, but as mentioned, the third-party application only accepts this data as a "comment", not as EXIF, which I cannot change. After reading this question, I looked into the binary structure of my sample file and found the comment at the start of the file, after a few bytes of some other (meta)data. I do however not know a lot about binary file manipulation, and also I was wondering if there is a more elegant way, other than messing with the binary...
EDIT: minimum example:
from PIL import Image
img = Image.open(path) # where path is the path to the sample image
# this prints the desired metadata if it is correctly saved in loaded image
print(img.info["comment"])
img.save(new_path) # save with different file name
img.close()
# now open to see if it has been saved correctly
new_img = Image.open(new_path)
print(new_img.info['comment']) # now results in KeyError
I also tried img.save(new_path, info=img.info), but this does not seem to have an effect. Since img.info['comment'] appears identical to img.app['COM'], I tried img.save(new_path, app=img.app), again does not work.
Just been having a play with this and I couldn't see anything directly in Pillow to support this. I've found that the save() method supports a parameter called extra that can be used to pass arbitrary bytes to the output file.
We then just need a simple method to turn a comment into a valid JPEG segment, for example:
import struct
from PIL import Image
def make_jpeg_variable_segment(marker: int, payload: bytes) -> bytes:
"make a JPEG segment from the given payload"
return struct.pack('>HH', marker, 2 + len(payload)) + payload
def make_jpeg_comment_segment(comment: bytes) -> bytes:
"make a JPEG comment/COM segment"
return make_jpeg_variable_segment(0xFFFE, comment)
# open source image
with Image.open("foo.jpeg") as im:
# save out with new JPEG comment
im.save('bar.jpeg', extra=make_jpeg_comment_segment("hello world".encode()))
# read file back in to ensure comment round-trips
with Image.open('bar.jpeg') as im:
print(im.app['COM'])
print(im.info['comment'])
Note that in my initial attempts I tried appending the comment segment at the end of the file, but Pillow wouldn't load this comment even after calling the .load() method to force it to load the entire JPEG file.
Update: The upcoming version Pillow version 9.4.0 will support this by passing a comment parameter while saving, e.g.:
with Image.open("foo.jpeg") as im:
im.save('bar.jpeg', comment="hello world")
hopefully that makes things easier!

Merge multiple base64 images into one

If I have multiple base64 strings that are images (one string = one image). Is there a way to combine them and decode to a single image file? i.e. from multiple base64 strings, merge and output a single image file.
I'm not sure how I would approach this using Pillow (or if I even need it).
Further clarification:
The source images are TIFFs that are encoded into base64
When I say "merge", I mean turning multiple images into a multi-page image like you see in a multi-page PDF
I dug through the Pillow documentation (v5.3) and found something that seems to work. Basically, there are two phases to this:
Save encoded base64 strings as TIF
Append them together and save to disk
Example using Python 3.7:
from PIL import Image
import io
import base64
base64_images = ["asdfasdg...", "asdfsdafas..."]
image_files = []
for base64_string in base64_images:
buffer = io.BytesIO(base64.b64decode(base64_string))
image_file = Image.open(buffer)
image_files.append(image_file)
combined_image = images_files[0].save(
'output.tiff',
save_all=True,
append_images=image_files[1:]
)
In the above code, I first create PIL Image objects from a bytes buffers in order to do this whole thing in-memory but you can probably use .save() and create a bunch of tempfiles instead if I/O isn't a concern.
Once I have all the PIL Image objects, I choose the first image (assuming they were in desired order in base64_images list) and append the rest of the images with append_images flag. The resulting image has all the frames in one output file.
I assume this pattern is extensible to any image format that supports the save_all and append_images keyword arguments. The Pillow documentation should let you know if it is supported.

Store a base64 image in python memory, then retrieve for use in wxpython/PIL

1) I have an image that I converted to a string. It looks like this:
bytesimage = b'iVBORw0KGgoAAAANSUhEUgA.... etc etc
2) I can convert it to an 'bytesimage.png' using:
def StringToImage(self, stringname, imageoutput):
imgdata = base64.b64decode(stringname)
imagename = imageoutput
with open(imagename, 'wb') as f:
f.write(imgdata)
3) But then I want to save that image or string to memory to use in wxpython interface without needing to save the file. I have seen several related questions where the solution is using io.BytesIO, but I just cant connect the steps and both wxpython or PIL don't seem to read the bytes properly.
So to clarify:
I have a image stored in a string DONE
I can convert that to an image (if needed) but dont want to save it DONE
I need that string OR image (whichever is best) saved to memory NEEDS SOLVING
Then I want to be able to use that image in wxpython (I can open in PIL first if required)
Any help would be fantastic!
StringIO seems to be the way to go. It allows you to pass the decoded string directly to PIL.
import base64
from PIL import Image
import StringIO
# Banana emoji (JPG) as a b64 string.
b64_img_str = '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'
# Decode back to the original bytes
new_img_str = base64.b64decode(b64_img_str)
# Use StringIO to provide an in-memory buffer that we can use
# to pass the image string to PIL.
sio = StringIO.StringIO(new_img_str)
img = Image.open(sio)
# Display the image
img.show()

Reading an image into pygame with incorrect file extension

I want to load a JPS file into pygame. A JPS file is simply a jpeg file with the image consisting of two side by side stereo pictures. While Pigame will load it in if I change the extension to jpg and use pygame.image.load(file_name), what I want to do is to load the file into memory and then tell Pigame to load the file in from a buffer and that the buffer contains a jpeg file.
I would like to do it this way because later I want to extend things so that I can load in an MPO file which is a file that contains two jpeg files and I suspect that the same techniques will be involved.
I have tried the pygame.image.frombuffer and pygame.image.fromstring but get the error message that the "String length does not equal format and resolution size". I think this is because I am not telling it that the buffer contains a jpeg.
Any one have any idea how this can be done?
Perhaps something along these lines (untested):
with open('image.jps', 'rb') as imgfile:
imgbuf = StringIO(imgfile.read())
image1 = pygame.image.load(imgbuf)
Since you say it works, you could probably shorten things as shown below since there's no reason to give the image buffer a name and keep it around:
with open('image.jps', 'rb') as imgfile:
image1 = pygame.image.load(StringIO(imgfile.read()))

How can a Pygame surface be converted to an image file say a jpeg or a png WITHOUT writing the file to disk? Say after a screen grab? [duplicate]

I have generated an image using PIL. How can I save it to a string in memory?
The Image.save() method requires a file.
I'd like to have several such images stored in dictionary.
You can use the BytesIO class to get a wrapper around strings that behaves like a file. The BytesIO object provides the same interface as a file, but saves the contents just in memory:
import io
with io.BytesIO() as output:
image.save(output, format="GIF")
contents = output.getvalue()
You have to explicitly specify the output format with the format parameter, otherwise PIL will raise an error when trying to automatically detect it.
If you loaded the image from a file it has a format property that contains the original file format, so in this case you can use format=image.format.
In old Python 2 versions before introduction of the io module you would have used the StringIO module instead.
For Python3 it is required to use BytesIO:
from io import BytesIO
from PIL import Image, ImageDraw
image = Image.new("RGB", (300, 50))
draw = ImageDraw.Draw(image)
draw.text((0, 0), "This text is drawn on image")
byte_io = BytesIO()
image.save(byte_io, 'PNG')
Read more: http://fadeit.dk/blog/post/python3-flask-pil-in-memory-image
sth's solution didn't work for me
because in ...
Imaging/PIL/Image.pyc line 1423 ->
raise KeyError(ext) # unknown
extension
It was trying to detect the format from the extension in the filename , which doesn't exist in StringIO case
You can bypass the format detection by setting the format yourself in a parameter
import StringIO
output = StringIO.StringIO()
format = 'PNG' # or 'JPEG' or whatever you want
image.save(output, format)
contents = output.getvalue()
output.close()
save() can take a file-like object as well as a path, so you can use an in-memory buffer like a StringIO:
buf = StringIO.StringIO()
im.save(buf, format='JPEG')
jpeg = buf.getvalue()
With modern (as of mid-2017 Python 3.5 and Pillow 4.0):
StringIO no longer seems to work as it used to. The BytesIO class is the proper way to handle this. Pillow's save function expects a string as the first argument, and surprisingly doesn't see StringIO as such. The following is similar to older StringIO solutions, but with BytesIO in its place.
from io import BytesIO
from PIL import Image
image = Image.open("a_file.png")
faux_file = BytesIO()
image.save(faux_file, 'png')
When you say "I'd like to have number of such images stored in dictionary", it's not clear if this is an in-memory structure or not.
You don't need to do any of this to meek an image in memory. Just keep the image object in your dictionary.
If you're going to write your dictionary to a file, you might want to look at im.tostring() method and the Image.fromstring() function
http://effbot.org/imagingbook/image.htm
im.tostring() => string
Returns a string containing pixel
data, using the standard "raw"
encoder.
Image.fromstring(mode, size, data) =>
image
Creates an image memory from pixel
data in a string, using the standard
"raw" decoder.
The "format" (.jpeg, .png, etc.) only matters on disk when you are exchanging the files. If you're not exchanging files, format doesn't matter.

Categories