I'm using scikit-image to read an image:
img = skimage.io.imread(filename)
After doing some manipulations to img, I'd like to save it to an in-memory file (a la StringIO) to pass off to another function, but it looks like skimage.io.imsave requires a filename, not a file handle.
I'd like to avoid hitting the disk (imsave followed by read from another imaging library) if at all possible. Is there a nice way to get imsave (or some other scikit-image-friendly function) to work with StringIO?
Update: 2020-05-07
We now recommend using the imageio library for image reading and writing. Also, with Python 3, StringIO changes to BytesIO:
from io import BytesIO
import imageio
buf = BytesIO()
imageio.imwrite(buf, image, format='png')
scikit-image stores images as numpy arrays, therefore you can use a package such as matplotlib to do so:
import matplotlib.pyplot as plt
from StringIO import StringIO
s = StringIO()
plt.imsave(s, img)
This may be worth adding as default behaviour to skimage.io.imsave, so if you want you can also file an issue at https://github.com/scikit-image/scikit-image.
Related
I load an image with img = imageio.imread('hello.jpg').
I want to return this numpy array as an image. I know I can do return FileResponse('hello.jpg'), however, in the future, I will have the pictures as numpy arrays.
How can I return the numpy array img from FastAPI server in a way that it is equivalent to return FileResponse('hello.jpg')?
You shouldn't be using StreamingResponse, as suggested by some other answer. If the entire image bytes are loaded into memory from the beginning (e.g., into an in-memory bytes buffer), using StreamingResponse makes little sense. Please have a look at this answer for more details. You should instead use Response and pass the image bytes, after converting the numpy array into a BytesIO buffered stream, as described in the documentation of the Imageio library that you are using—if you instead wish to use PIL or Pillow library (the successor of PIL, which added Python 3.x support), see this answer. You can also define the media_type, as well as set the Content-Disposition header, as described here and here, so that the image is viewed in the browser (if you would like to have the image downloaded rather than viewed in the browser, then use attachment insetad of inline, as described in the linked answers above). Example below:
import io
import imageio
from imageio import v3 as iio
from fastapi import Response
#app.get("/image", response_class=Response)
def get_image():
im = imageio.imread("test.jpeg") # 'im' could be an in-memory image (numpy array) instead
with io.BytesIO() as buf:
iio.imwrite(buf, im, plugin="pillow", format="JPEG")
im_bytes = buf.getvalue()
headers = {'Content-Disposition': 'inline; filename="test.jpeg"'}
return Response(im_bytes, headers=headers, media_type='image/jpeg')
You can use StreamingResponse (https://fastapi.tiangolo.com/advanced/custom-response/#using-streamingresponse-with-file-like-objects) to do it e.g., but before you will need to convert your numpy array to the io.BytesIO or io.StringIO
Through some http requests I have been able to receive an image in binary form as
b'\xff\xd8\xff\xe0\x00\...
and with:
with open('image.jpg', 'wb') as out_file:
out_file.write(binary_content)
where binary_content is a string containing the data received through request I saved an image into a file.
Afterwards I can read this image with OpenCV methods. But I wanted to do a direct pass from binary string to OpenCV Mat without any in-betweens. cv2.decode method didn't work.
io.BytesIO and PIL worked well. Closing this q.
If you want to stay in the SciPy ecosystem, then the imageio library (previously part of SciPy) works well.
from imageio import imread
image_array = imread("image_path.jpg")
The code above gives you an uint8 array, if you want a float array, you can cast it easily
from imageio import imread
image_array = imread("image_path.jpg").astype(float)
I'd like to work directly with compressed JPEG images. I know that with PIL/Pillow I can compress an image when I save it, and then read back the compressed image - e.g.
from PIL import Image
im1 = Image.open(IMAGE_FILE)
IMAGE_10 = os.path.join('./images/dog10.jpeg')
im1.save(IMAGE_10,"JPEG", quality=10)
im10 = Image.open(IMAGE_10)
but, I'd like a way to do this without the extraneous write and read. Is there some Python package with a function that will take an image and quality number as inputs and return a jpeg version of that image with the given quality?
For in-memory file-like stuff, you can use StringIO.
Take a look:
from io import StringIO # "import StringIO" directly in python2
from PIL import Image
im1 = Image.open(IMAGE_FILE)
# here, we create an empty string buffer
buffer = StringIO.StringIO()
im1.save(buffer, "JPEG", quality=10)
# ... do something else ...
# write the buffer to a file to make sure it worked
with open("./photo-quality10.jpg", "w") as handle:
handle.write(buffer.contents())
If you check the photo-quality10.jpg file, it should be the same image, but with 10% quality as the JPEG compression setting.
Using BytesIO
try:
from cStringIO import StringIO as BytesIO
except ImportError:
from io import BytesIO
def generate(self, image, format='jpeg'):
im = self.generate_image(image)
out = BytesIO()
im.save(out, format=format,quality=75)
out.seek(0)
return out
StringIO is missing in Python3.0, ref to : StringIO in python3
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.
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.