resized_image = Image.resize((100,200));
Image is Python-Pillow Image class, and i've used the resize function to resize the original image,
How do i find the new file-size (in bytes) of the resized_image without having to save to disk and then reading it again
The file doesn't have to be written to disk. A file like object does the trick:
from io import BytesIO
# do something that defines `image`...
img_file = BytesIO()
image.save(img_file, 'png')
print(img_file.tell())
This prints the size in bytes of the image saved in PNG format without saving to disk.
You can't. PIL deals with image manipulations in memory. There's no way of knowing the size it will have on disk in a specific format.
You can save it to a temp file and read the size using os.stat('/tmp/tempfile.jpg').st_size
Related
Basically, I want to add a few bytes to my new PNG image file. An example case is like the following code:
img = open("example.png", "rb") # Open images and read as binnary
hex = img.read().hex() # Read images as Bytes to Hexadecimal.
add_hex = hex+"7feab1e74a4bdb755cca" # Add some bytes to it (as hex)
to_bytes_img = bytes.fromhex(add_hex) # Convert hex to bytes
with open("example2.png", "wb") as f: # Write images
f.write(to_bytes_img)
But, the problem is, I have a special case that requires me to perform the above operation using OpenCV (CV2). Where cv2.imread() only reads and stores Pixels as a numpy array (an array of pixels, not the whole file).
Then, I want to write that image into a new file cv2.imwrite(), which will rebuild the image and save the PNG on disk. My question is, how do I add some bytes to the PNG image file (in buffer/memory), before the cv2.imwrite() operation.
I could probably do it with with open() as above, but that would be very inefficient opening, writing, opening, writing to disk again.
I read a multi-frame DICOM file with pydicom, after that I write it into a new file. However when I open the DICOM file, the image has an offset/shift.
ds = pydicom.dcmread('./Multiframe/0020.dcm')
arr = ds.pixel_array
ds.PixelData = encapsulate([arr[0].tobytes(),
arr[1].tobytes(),
arr[2].tobytes(),
arr[3].tobytes(),
arr[4].tobytes(),
arr[5].tobytes(),
arr[6].tobytes(),
arr[7].tobytes(),
arr[8].tobytes(),
arr[9].tobytes(),
arr[10].tobytes()])
ds.save_as('new.dcm', write_like_original=False)
What is wrong with the code? It works if I write a simple image (not multi-frame), the problem is with the encapsulation.
Original:
After:
Encapsulation of Pixel Data (both single and multi-framed) is only required for compressed transfer syntaxes, such as JPEG or RLE Lossless. If you have an uncompressed syntax such as Explicit VR Little Endian then no encapsulation is necessary:
ds = pydicom.dcmread('./Multiframe/0020.dcm')
arr = ds.pixel_array
if ds.file_meta.TransferSyntaxUID.is_compressed:
raise AttributeError("Encapsulation required for compressed transfer syntaxes")
ds.PixelData = arr.tobytes()
ds.save_as('new.dcm', write_like_original=False)
How can I get the same effect as:
from PIL import Image
with Image.open(image_path) as image:
image.thumbnail((200, 200), Image.ANTIALIAS)
image.save(temporary_thumbnail_path)
with open(temporary_thumbnail_path, "rb") as thumbnail_file:
thumbnail_as_string = base64.b64encode(thumbnail_file.read()).decode()
without having to write to disk ?
i.e. I would like to get the bytes representation of the compressed image, but without having to resort to temporary_thumbnail_path.
I know that PIL documentation recommends using
save(), with a BytesIO parameter for in-memory data.
but I am not sure to understand what this means and haven't found examples online.
It was not so hard:
import io
from PIL import Image
output = io.BytesIO()
with Image.open(image_path) as image:
image.thumbnail((400, 400), Image.ANTIALIAS)
image.save(output, format="JPEG")
thumbnail_as_string = base64.b64encode(output.getvalue()).decode()
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.