Python reportlab generates large files when I add jpeg - python

I try to generate pdf filled up with jpeg images, but even when I add only one 176 kB (1142 × 1713) image, I get 2.5 mB pdf. When I open this pdf in Photoshop, I see that this is the same jpeg on a canvas, with the same size. Looks like reportlab doesn't do any compression for jpeg or something. There's no any information about it in the documentation.
Here's my code:
...
from reportlab.pdfgen import canvas
from reportlab.lib.utils import ImageReader
from reportlab.lib.units import mm
from PIL import Image
img = Image.open('/Users/dimabogdan/Downloads/3edbc3086ccadcae8c59a702a4e03020.jpg')
# <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=1142x1713 at 0x10A74BE48>
img = ImageReader(img)
c = canvas.Canvas('pdf.pdf', pagesize=(320*mm, 470*mm))
c.drawImage(img, x=0, y=0, height=152*mm, width=102*mm)
c.save()
...
How can I solve this issue?

I just came across this same issue, and for anyone still having this problem the code:
img = ImageReader(img)
c.drawImage(img, x=0, y=0, height=152*mm, width=102*mm)
creates a PDF with uncompressed image data, however you can compress the image with jpeg into a temporary buffer and feed it to reportlab and it will save it compressed, like so:
with BytesIO() as img_jpg: # we create a new BytesIO object, don't forget 'from io import BytesIO'
img.save(img_jpg, format='jpeg') # img is the image from Image.open, we save it to the buffer
img_jpg.seek(0) # reset the buffer position
img_ = ImageReader(img_jpg) # create ImageReader from buffer
c.drawImage(img, x=0, y=0, height=152*mm, width=102*mm) # draw compressed jpeg to PDF
Your sample code should work fine, since you are feeding a jpeg image too, but it doesn't...

Related

Pysimplegui resizing images

I'm trying to resize images in pysimplegui however it crops the images instead of resizing.
My image element is written as:
ui.Image('{filename}'), size=(50,50)))
Which results to something like:
While the original looks like:
I've seen somewhere else that suggests PIL (link). However, this looks a lot longer than i liked and was wondering if there is an easier way to do this.
Peace
hi
to resize an image you need to take advantage of the pillow library, but you need to import other libraries too in order to convert it into bytes if needed, here is an example:
import PIL.Image
import io
import base64
def resize_image(image_path, resize=None): #image_path: "C:User/Image/img.jpg"
if isinstance(image_path, str):
img = PIL.Image.open(image_path)
else:
try:
img = PIL.Image.open(io.BytesIO(base64.b64decode(image_path)))
except Exception as e:
data_bytes_io = io.BytesIO(image_path)
img = PIL.Image.open(data_bytes_io)
cur_width, cur_height = img.size
if resize:
new_width, new_height = resize
scale = min(new_height/cur_height, new_width/cur_width)
img = img.resize((int(cur_width*scale), int(cur_height*scale)), PIL.Image.ANTIALIAS)
bio = io.BytesIO()
img.save(bio, format="PNG")
del img
return bio.getvalue()
ui.Image(key="-PHOTO-",size=(50,50) #after some change
elif event == "-IMG-": # the"-IMG-" key is in [ui.I(key="IMG",enable_events=True), ui.FileBrowse()]
window['-PHOTO-'].update(data=resize_image(value["-IMG-"],resize=(50,50)))
I hope this helps
Helloooo, heres my workaround to resize images in pysimplegui:
read the image stored in the path 'old_path'.
resize this image to my desired dimensions.
store the resized image in a folder as a 'png' file.
finally display the resized image.
old_path = os.path.join(
values["-FOLDER-"], values["-FILE LIST-"][0]
)
# read image using old_path
im = cv2.imread(old_path)
# resize image to desired dimensions
im = cv2.resize(im,[700,500])
# save image to temporary folder (new_path) as png
new_path ='temp_storage/image_to_show.png'
cv2.imwrite(new_path,im)
# update window with new resized image
window["-IMAGE-"].update(new_path)
if you need the full code let me know. The image storing folder only stores the image to be shown, it will override every time you choose a new image so no worries about images pilling up.
cv2 needed for reading, resizing and writing. (or PIL)
Goodluck!

Get compressed image byte representation in memory

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

Tkinter: saving image results in quality lost

I read a picture and display it in a Canvas:
im=the_canvas.create_image(0,0,anchor="nw",image=img)
After that, I save it:
returned_value=the_canvas.postscript(file="saved.ps", height=image_h, width=image_w, colormode="color")
r=Image.open("saved.ps")
r.save("saved.png")
How can I save the image in png or jpg formats without loosing its quality ?
Thank you in advance.
You can use this code:
from PIL import Image,ImageTK
save_name=filedialog.asksaveasfilename()
canvas.postscript(file=save_name+".eps") # save canvas as encapsulated postscript
img = Image.open(save_name+".eps")
img.save(save_name+".png", "png",quality=99)
This will save the image as a png.

How to get image size in python-pillow after resize?

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

How to convert a Pyglet image to a PIL image?

i want to convert a Pyglet.AbstractImage object to an PIL image for further manipulation
here are my codes
from pyglet import image
from PIL import Image
pic = image.load('pic.jpg')
data = pic.get_data('RGB', pic.pitch)
im = Image.fromstring('RGB', (pic.width, pic.height), data)
im.show()
but the image shown went wrong.
so how to convert an image from pyglet to PIL properly?
I think I find the solution
the pitch in Pyglet.AbstractImage instance is not compatible with PIL
I found in pyglet 1.1 there is a codec function to encode the Pyglet image to PIL
here is the link to the source
so the code above should be modified to this
from pyglet import image
from PIL import Image
pic = image.load('pic.jpg')
pitch = -(pic.width * len('RGB'))
data = pic.get_data('RGB', pitch) # using the new pitch
im = Image.fromstring('RGB', (pic.width, pic.height), data)
im.show()
I'm using a 461x288 image in this case and find that pic.pitch is -1384
but the new pitch is -1383
This is an open wishlist item:
AbstractImage to/from PIL image.

Categories