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() )
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'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.
we are using a Raspberry Pi + Python 3.4 + PyGame to capture an image from a specific USB webcam. We use this simple code to capture (it works ok):
pygame.camera.init()
cam = pygame.camera.Camera(pygame.camera.list_cameras()[0],(1280,720))
cam.start()
time.sleep(1)
webcamImage = cam.get_image()
The problem comes here: we have to convert this webcamImage into a PIL image. We follow this link but unfortunately the function Image.fromstring() not exists anymore. So, we can't do that:
pil_string_image = pygame.image.tostring(webcamImage, "RGBA",False)
pil_image = Image.fromstring("RGBA",(1280,720),pil_string_image)
PIL says that Image.fromstring() is deprecated, and suggests to use the function Image.frombytes(). Clearly we not found the equivalent pygame.image function that convert the webcamImage into an array of bytes. So we are stucked here: can you help us, please?
Thank you :-)
As per Damian Yerrick's comment, under Python 3 the result of pygame.image.tostring() is a bytes, despite the method's name. Thus we can go out of this situation with this simple code:
pygame.camera.init()
cam = pygame.camera.Camera(pygame.camera.list_cameras()[0],(1280,720))
cam.start()
time.sleep(1)
webcamImage = cam.get_image()
pil_string_image = pygame.image.tostring(webcamImage,"RGBA",False)
im = Image.frombytes("RGBA",(1280,720),pil_string_image)
What imaging modules for python will allow you to take a specific size screenshot (not whole screen)?
I have tried PIL, but can't seem to make ImageGrab.grab() select a small rectangle
and i have tried PyGame but i can't make it take a screen shot outside of it's main display panel
You can use pyscreenshot module.
The pyscreenshot module can be used to copy the contents of the screen to a PIL image memory or file.
You can install it using pip.
$ sudo pip install pyscreenshot
Usage:
import pyscreenshot as ImageGrab
# fullscreen
im=ImageGrab.grab()
im.show()
# part of the screen
im=ImageGrab.grab(bbox=(10,10,500,500))
im.show()
# to file
ImageGrab.grab_to_file('im.png')
I have tried PIL, but can't seem to make ImageGrab.grab() select a small rectangle
What did you try?
As the documentation for ImageGrab clearly states, the function has a bbox parameter, and:
The pixels inside the bounding box are returned as an “RGB” image. If the bounding box is omitted, the entire screen is copied.
So, you only get the whole screen if you don't pass a bbox.
Note that, although I linked to the Pillow docs (and you should be using Pillow), old-school PIL's docs say the same thing:
The bounding box argument can be used to copy only a part of the screen.
So, unless you're using a really, really old version of PIL (before 1.1.3, which I believe is more than a decade out of date), it has this feature.
1) Use pyscreenshot, ImageGrab works but only on Windows
2) Grab the image and box it, then save that image
3) Don't use ImageGrab.grab_to_file, it saves the full size image
4) You don't need to show the image with im.show if you just want to save a screenshot
import pyscreenshot as ImageGrab
im=ImageGrab.grab(bbox=(10,10,500,500))
im.save('im.png')
You could use Python MSS.
From documentation to capture only a part of the screen:
import mss
import mss.tools
with mss.mss() as sct:
# The screen part to capture
monitor = {"top": 160, "left": 160, "width": 160, "height": 135}
output = "sct-{top}x{left}_{width}x{height}.png".format(**monitor)
# Grab the data
sct_img = sct.grab(monitor)
# Save to the picture file
mss.tools.to_png(sct_img.rgb, sct_img.size, output=output)
print(output)
You can use pyscreenshot at linux or windows platforms . I am using Ubuntu it works for me. You can force if subprocess is applied setting it to false together with mss gives the best performance.
import pyscreenshot as ImageGrab
import time
t1 = time.time()
imgScreen = ImageGrab.grab(backend="mss", childprocess=False)
img = imgScreen.resize((640,480))
img.save("screen.png")
t2 = time.time()
print("The passing time",(t2-t1))