I've run into an issue I can't seem to figure out with PIL and reportlab. Specifically, I would like to use drawImage on a canvas in reportlab using a PIL Image object.
In the past I've inserted images into reportlab documents from the web using raw data, StringIO and reportlab's ImageReader class. Unfortunately, ImageReader takes a file name or a file buffer like object.
The ultimate goal is to be able to put QR codes, (which are PIL objects) into the reportlab PDFs. One thing that does work is the following:
size, qrcode = PyQrcodec.encode('http://www.google.com')
qrcode.save("img.jpeg")
self.pdf.drawImage(ImageReader("img.jpeg"), 25, 25, width=125, height=125)
self.pdf.showPage()
This saves the image and then reads it into the pdf. Obviously doing it like this doesn't make sense.
My efforts are compounded by the relatively long development history of reportlab which makes finding the answers relevant to the latest version (2.4).
Thanks for the help.
(By the way, I'm using 1.1.6 PIL)
Although it does look like it should work, it really doesn't. I finally was able to track down the problem, and it was in the _isPILImage() function. The problem is that "Image.Image" is actually "from PIL import Image" whereas my object is actually just from Image. I would have assumed they were the same, but in any case isinstance doesn't evaluate them as the same. My hack solution was to change _isPILImage(fileName): ... to
519 def _isPILImage(im):
520 import Image as PIL_Image
521 try:
522 return isinstance(im,Image.Image) or isinstance(im, PIL_Image.Image)
523 except ImportError:
524 return 0
That solves my error. Since you pointed me in the right direction I originally tried to post this as a comment then accept your answer, but it doesn't allow enough characters.
Thank you for the input! If you can think of a more elegant way to fix this... (I tried to wrap the Image.Image object in a PIL Image object) let me know!
Looking at the source for ReportLab 2.4, it seems that ImageReader will accept a PIL Image object as "filename".
def _isPILImage(im):
try:
return isinstance(im,Image.Image)
except ImportError:
return 0
class ImageReader(object):
"Wraps up either PIL or Java to get data from bitmaps"
_cache={}
def __init__(self, fileName):
if isinstance(fileName,ImageReader):
self.__dict__ = fileName.__dict__ #borgize
return
#start wih lots of null private fields, to be populated by
#the relevant engine.
self.fileName = fileName
self._image = None
self._width = None
self._height = None
self._transparent = None
self._data = None
if _isPILImage(fileName):
self._image = fileName
self.fp = getattr(fileName,'fp',None)
try:
self.fileName = self._image.fileName
except AttributeError:
self.fileName = 'PILIMAGE_%d' % id(self)
weired
the documentation claims that drawImage and drawInlineImage work the same way, but it works with drawInlineImage out of the box, and do not work in drawImage
This is what I did, using plotly, BytesIO and reportlab. It puts an image in the pdf from memory, without having to save it on disk first.
import io
import plotly.graph_objects as go
from reportlab.platypus import SimpleDocTemplate, Image
image_in_memory = io.BytesIO()
figure = go.Figure(data=data) # as it's not part of the question I'm leaving data out
figure.write_image(image_in_memory, scale=5, width=1000, height=400)
pdf_image = Image(image_in_memory, width=400, height=160)
pdf_document = SimpleDocTemplate(path, title='Title')
pdf_document.multiBuild([pdf_image])
Related
Hours of searching keep turning up this code:
mywxImage = wx.EmptyImage(*size*)
myPILImageRGB = MyPILImage.convert('RGB')
myPILImageData = MyPILImageRGB.tostring()
mywxImage.SetData(myPILImageData)
But MyPILImageRGB doesn't seem to have a tostring() method.
What I'm trying to do is display images in a Python program without using an external application.
You do not need to use the tostring method at all.
This code converts a pil image to a bitmap and creates a wxImage on the way
def static_bitmap_from_pil_image(self, pil_image):
wx_image = wx.Image(pil_image.size[0], pil_image.size[1])
wx_image.SetData(pil_image.convert("RGB").tobytes())
bitmap = wx.Bitmap(wx_image)
static_bitmap = wx.StaticBitmap(self, wx.ID_ANY, wx.NullBitmap)
static_bitmap.SetBitmap(bitmap)
return static_bitmap
I am quite confused cause when I try to save the resized version of an image it says 'AttributeError: 'NoneType' object has no attribute 'save''.
I looked over the internet and also to this question: Python Pillow's thumbnail method returning None but i already used the save function so i don't get why it doesn't work.
Here's my code:
from PIL import Image
imgg = Image.open('cropped.tif')
new_image = imgg.thumbnail((400, 400))
new_image.save('thumbnail_400.tif')
I bet it's something stupid but i can't see what it is. I appreciate any help.
thumbnail() is an extension method without a return object. The new_image variable will stay None in your case. You need to do this.
from PIL import Image
imgg = Image.open('cropped.tif')
imgg.thumbnail((400, 400))
imgg.save('thumbnail_400.tif')
I'm using latest PyQt5 5.12.2 and I'm getting a weird message for every JPG picture that I'm showing in my script using QPixmap or QIcon.
qt.gui.icc: fromIccProfile: failed minimal tag size sanity
It isn't causing anything and the script works as it should. The problem is that I'm trying to display a huge amount of jpg pictures at the same time (as a photo gallery) so the window gets unresponsive until all messages are printed for each photo.
I tried for hours to find something useful online but unfortunately, it seems like nearly no one had the same issue. I'm using some PNG files too and they don't raise this error so I'm assuming that the problem is with jpg format. I tried using older pyqt5 versions but the only difference is that they are not printing the message but the problem remains.
Meanwhile, I tried to use this command to mute those messages since there is no use of them but the problem with unresponsive window for a few seconds remains even when it's not printing in the console.
def handler(*args):
pass
qInstallMessageHandler(handler)
EDIT: I tried converting these images to PNG but the error remains. So the JPG format wasn't the problem
I dug more deeply into ICC profiles and colour spaces and it seems like the colour space that your pictures are using is somehow non-standard for PyQt.
My solution is to convert these pictures to an ICC profile that is classical such as sRGB.
Here's an example function:
import io
from PIL import Image, ImageCms
def convert_to_srgb(file_path):
'''Convert PIL image to sRGB color space (if possible)'''
img = Image.open(file_path)
icc = img.info.get('icc_profile', '')
if icc:
io_handle = io.BytesIO(icc) # virtual file
src_profile = ImageCms.ImageCmsProfile(io_handle)
dst_profile = ImageCms.createProfile('sRGB')
img_conv = ImageCms.profileToProfile(img, src_profile, dst_profile)
icc_conv = img_conv.info.get('icc_profile','')
if icc != icc_conv:
# ICC profile was changed -> save converted file
img_conv.save(file_path,
format = 'JPEG',
quality = 50,
icc_profile = icc_conv)
Using PIL library is a fast and effective way how to properly resolve that error.
I am making a GUI Image Viewer with Pyside2 and was having a similar issue.
The images were loading fine and for my case there was no performances issues, but I keep getting these ICC warnings.
And I didn't want to fix the original files, because my app is supposed to be only a viewer.
I don't know it will help for your case, but my solution is to first load the image with pillow ImageQT module
from pathlib import Path
from PIL.ImageQt import ImageQt
def load_image(path):
if Path(path).is_file():
return ImageQt(path)
Then in my QT Widget class that display the image, I load this image on a empty QPixmap:
def on_change(self, path):
pixmap = QtGui.QPixmap()
image = load_image(path)
if image:
pixmap.convertFromImage(image)
if pixmap.isNull():
self.display_area_label.setText('No Image')
else:
self.display_area_label.setPixmap(pixmap)
So I have been having problems trying to write the function to change the size of an image if to big and saving it as a thumbnail. I have how to retrieve the image just lost after that. I know about pillow but cant use for the class any help would be appreciated.
Update: So far I have gotten the code to resize the image and make it a thumbnail. The next part that I am on is having it save if resized to thumbnail2, but if it stays the same save as thumbnail1. Here is my code so far without the next step.
import urllib
url ="https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstra ion_1.png"
src = "C:\Users\laramie\Pictures\PNG_transparency_demonstration_1.png"
connect = urllib.urlretrieve(url, src)
def scalePicture(src):
newWidth = getWidth(src)/2
newHeight = getHeight(src)/2
canvas = makeEmptyPicture(newWidth, newHeight)
for x in range(newWidth):
for y in range(newHeight):
setColor(getPixel(canvas, x,y), getColor(getPixel(src, x*2, y*2)))
return canvas
def thumbNail():
srcPic = makePicture(src)
destWidth = getWidth(srcPic) / 2
destHeight = getHeight(srcPic) / 2
destPic = makeEmptyPicture(destWidth, destHeight)
destPic = scalePicture(srcPic)
show(srcPic)
show(destPic)
thumbNail()
There are a bunch of strange things going on in your code:
destPic = makeEmptyPicture(destWidth, destHeight)
destPic = scalePicture(srcPic)
the first line here is not required, because the destPic is overwritten immediately.
for x in range(newWidth):
for y in range(newHeight):
setColor(getPixel(canvas, x,y), getColor(getPixel(src, x*2, y*2)))
Ths is a very inefficient way to scale an image, that gives inferior results, unless the scale factor is an integer, and even then there are faster and better approaches.
I would recommend you to import PIL (Python Image Library) and use it to work with images. Things like loadng, saving, scaling or flipping images are easily done. However, you may need to install this library if it did not come with your python installation.
I just want to display python image object in wxpython panel.
What i am trying to accomplish is taking screenshot using python ImageGrab or autopy and showing it in wxpython panel. My screenshot program running every second so there is no point save the image for wx.
import pyscreenshot as ImageGrab
imgobj = ImageGrab.grab()
print imgobj
# Convert into wximage
myWxImage = wx.EmptyImage( imgobj.size[0], imgobj.size[0] )
myWxImage.SetData( imgobj.convert( 'RGB' ).tostring())
Output
<Image.Image image mode=RGB size=1366x768 at 0x20B98F0>
ValueError:Invalid data buffer size.
It's not easy to tell without a full traceback, but I think the issue might be this typo:
myWxImage = wx.EmptyImage( imgobj.size[0], imgobj.size[0] )
Should be:
myWxImage = wx.EmptyImage( imgobj.size[0], imgobj.size[1] )
Also you could make your code simpler with:
myWxImage = wx.ImageFromBuffer(imgobj.size[0], imgobj.size[1], imgobj.convert('RGB').tostring())
I assume your problem is that you are passing the image width as new height to wx...
And btw instead you could use
myWxImage = wx.ImageFromBuffer( imgobj.size[0], imgobj.size[1], imgobj.tostring() )