Make a GIF play exactly once with Python Wand - python

I can create an animated GIF like this:
from wand.image import Image
with Image() as im:
while i_need_to_add_more_frames():
im.sequence.append(Image(blob=get_frame_data(), format='png'))
with im.sequence[-1] as frame:
frame.delay = calculate_how_long_this_frame_should_be_visible()
im.type = 'optimize'
im.format = 'gif'
do_something_with(im.make_blob())
However, an image created like this loops indefinitely. This time, I want it to loop once, and then stop. I know that I could use convert's -loop parameter if I were using the commandline interface. However, I was unable to find how to do this using the Wand API.
What method should I call, or what field should I set, to make the generated GIF loop exactly once?

You'll need to use ctypes to bind the wand library to the correct C-API method.
Luckily this is straightforward.
import ctypes
from wand.image import Image
from wand.api import library
# Tell Python about the C-API method.
library.MagickSetImageIterations.argtypes = (ctypes.c_void_p, ctypes.c_size_t)
with Image() as im:
while i_need_to_add_more_frames():
im.sequence.append(Image(blob=get_frame_data(), format='png'))
with im.sequence[-1] as frame:
frame.delay = calculate_how_long_this_frame_should_be_visible()
im.type = 'optimize'
im.format = 'gif'
# Set the total iterations of the animation.
library.MagickSetImageIterations(im.wand, 1)
do_something_with(im.make_blob())

Related

pyautogui live image locator

i've got a problem in python :
import pyautogui as a
while True:
pixel = a.locateOnScreen("example.png")
if pixel == None: continue
pixel = a.center(pixel)
data = [pixel.x , pixel.y]
a.moveTo(data[0],data[1])
in this code it finds the picture and move the mouse on it but its to slow because every time loop start from beginning it loads the file and it makes it too slow.
i want it to work lively.
i tried:
import pyautogui as a
from IPython.display import Image
f = Image("example.png")
while True:
pixel = a.locateOnScreen(f)
pixel = a.center(pixel)
data = [pixel.x , pixel.y]
a.moveTo(data[0],data[1])
but it says image don't have attribute named mode
I want to place pointer on the center of picture
You use IPython.display.Image object, but that is the wrong one. Use PIL.Image instead:
import pyautogui as a
from PIL import Image
f = Image.open("example.png")
while True:
pixel = a.locateOnScreen(f)
pixel = a.center(pixel)
data = [pixel.x , pixel.y]
a.moveTo(data[0],data[1])
I confirmed this works with pyautogui version 0.9.53
That said, I don't think that the loading of the image is your performance bottleneck here, but rather the search algorithm itself. Pyautogui's documentation states that locateOnScreen can take some time. You can try to search a smaller region on the screen. Citing:
These “locate” functions are fairly expensive; they can take a full
second to run. The best way to speed them up is to pass a region
argument (a 4-integer tuple of (left, top, width, height)) to only
search a smaller region of the screen instead of the full screen:
import pyautogui
pyautogui.locateOnScreen('someButton.png', region=(0,0, 300, 400))

wand: How to assemble transparent gif / clear background each frame

So I have a series of transparent pngs and append them to a new Image()
with Image() as new_gif:
for img_path in input_images:
with Image(filename=img_path) as inimg:
# create temp image with transparent background to composite
with Image(width=inimg.width, height=inimg.height, background=None) as new_img:
new_img.composite(inimg, 0, 0)
new_gif.sequence.append(new_img)
new_gif.save(filename=output_path)
unfortunately the background is not "cleared" when the new image is appended. They'll have the last image there as well:
But how do I clear the background? I though I do exactly that by compositing into a new image upfront.. `:| HALP!!
I see there is a similar thing with commandline ImageMagick but wand doesn't have anything like that. So far I have to workaround with a fitting background color.
Without seeing the source images, I can assume the -set dispose background is what's needed. For wand, you'll need to call wand.api.library.MagickSetOption method.
from wand.image import Image
from wand.api import library
with Image() as new_gif:
# Tell new gif how to manage background
library.MagickSetOption(new_gif.wand, 'dispose', 'background')
for img_path in input_images:
library.MagickReadImage(new_gif.wand, img_path)
new_gif.save(filename=output_path)
Or alternatively...
You can extent wand to manage Background Dispose behavior. This approach would give you the benefit of alter/generate each frame programmatically. But the down side would include a lot more work with ctypes. For example.
import ctypes
from wand.image import Image
from wand.api import library
# Tell python about library method
library.MagickSetImageDispose.argtypes = [ctypes.c_void_p, # Wand
ctypes.c_int] # DisposeType
# Define enum DisposeType
BackgroundDispose = ctypes.c_int(2)
with Image() as new_gif:
for img_path in input_images:
with Image(filename=img_path) as inimg:
# create temp image with transparent background to composite
with Image(width=inimg.width, height=inimg.height, background=None) as new_img:
new_img.composite(inimg, 0, 0)
library.MagickSetImageDispose(new_img.wand, BackgroundDispose)
new_gif.sequence.append(new_img)
# Also rebuild loop and delay as ``new_gif`` never had this defined.
new_gif.save(filename=output_path)
<- still needs delay correction

Access qrpython images without writing them to file

Here's my problem.
I'm building a simple QR-code generator with qrcode and PyQt4. In particular, this application shows an image of the generated QR-code inside a QPixmap of a QLabel. The problem is that the qrmodule, as far as I know, allows only to save the generated image to a file. I tried to access the inner workings of qrcode but it's kind of convoluted.
Do you know if it's possible to expose the image by saving the resulting image in a file stream, like the one from tempfile.TemporaryFile()? Otherwise I can only insert the qr-code by saving it on a real file and then loading it. For example
import qrcode as q
from PyQt4 import QtGui
filename = 'path/to/file.png'
img = q.make('Data')
img.save(filename)
pixmap = QtGui.QPixmap(filename)
EDIT 1
I tried to use the PIL.ImageQt.ImageQt function as proposed in an answer in the following way
import sys
import qrcode as qr
from PyQt4 import QtGui
from PIL import ImageQt
a = QtGui.QApplication(sys.argv)
l = QtGui.QLabel()
pix = QtGui.QPixmap.fromImage(ImageQt.ImageQt(qr.make("Some test data")))
l.setPixmap(pix)
l.show()
sys.exit(a.exec_())
The result is not however consistent. This is the result obtained using the method above
And this is the result using qrcode.make('Some test data').save('test2.png')
Am I missing something? The ImageQt function si a subclass of QImage as far as I understand, in fact I get no runtime errors, but the image is corrupted.
It turns out it takes a couple of steps to do this but you don't need to mess around with intermediate files. Note that the QRcode that qrcode generates is a PIL (python image library but its best to use the fork pillow) object. PIL provides the handy ImageQt module to convert your QR code into an Qimage object. Next you need to use the method QtGui.QPixmap.fromImage to create a pixmap (this method seg faults unless qt's initialisation has been done). The complete code would look something like this:
>>> import qrcode as q
>>> from PyQt4 import QtGui
>>> from PIL import ImageQt
>>> import sys
>>> app = QtGui.QApplication(sys.argv) # line 8 seg faults without this
>>> qrcode = q.make("some test data")
>>> qt_image = ImageQt.ImageQt(qrcode)
>>> pixmap = QtGui.QPixmap.fromImage(qt_image)
Obviously you can neaten that up somewhat but it works.

Scaling up an animated GIF in python to be used with PyGTK

I have this little python script which opens up a window and puts an animated GIF inside the window. I'm using it as a screensaver (nothing fancy, really). My problem is that the GIF isn't the size of my monitor; it's quite small on the screen. What I'd like to do is to scale it to the size of the screen. I have looked into PIL as well as images2gif, but they don't give me the scaling options I require.
What I would like to do is load the animated GIF in the script, then scale it, and finally play in the window generated by PyGTK. What I've tried doing is using subprocess to capture the output from gifsicle. While this does scale the GIF I can't use it in the PyGTK part of the script because of the data returned from subprocess. What I do is:
p = subprocess.check_output(["gifsicle --scale 3.3 foo.gif"], shell=True)
The variable p has the animated GIF data, but I can't use with PyGTK in that condition. Ideally I would like to load it like this:
pixbufAn = gtk.gdk.pixbufAnimation(p)
image = gtk.Image()
image.set_from_animation(pixbufAn)
Is there a way for me to use the data from the gifsicle --scale 3.3 foo.gif call? Or is there a pure Python way of scaling the animated GIF and using it with PyGTK?
logically you can use
GdkPixbuf.Pixbuf.scale_simple ()
on all the GdkPixbuf contains within GdkPixbuf.PixbufAnimation. How to get the single GdkPixbuf.Pixbuf? Use the iter to walk through GdkPixbuf.PixbufAnimation. first iter would be
iter = GdkPixbuf.PixbufAnimation.get_iter (GLib.get_current_time() )
pbuf = iter.get_pixbuf ()
# scale the pbuf below
......
# make this on a loop
iter = iter.advance (GLib.get_current_time())
# after getting all the pbuf, pack again into GdkPixbuf.PixbufSimpleAnim
simpleanim = GdkPixbuf.PixbufSimpleAnim (width, height, rate)
# loop again
simpleanim.add_frame (pbuf)
# after done all the chores, call the
Gtk.Image.set_from_animation (simpleanim) #simpleanim implemented anim

videocapture and pyqt

i use this lib http://videocapture.sourceforge.net/ for a capturing web cam. But i don't understand how it video-stream send to qpixmap.
If you check out the documentation for this library, you will see its very short and sweet. http://videocapture.sourceforge.net/html/VideoCapture.html
There are two ways I can tell you that you could get your image into Qt...
The best way - Directly into a QImage
getImage() states that it will return a PIL image. PIL, if you are using the latest version, has a module called ImageQt which can take a PIL Image object and give you back a QImage. From here you could convert that to QPixmap:
from PyQt4 import QtCore, QtGui
from VideoCapture import Device
from PIL import Image, ImageQt
app = QtGui.QApplication([])
cam = Device()
# this is a PIL image
pilImage = cam.getImage()
# this is a QImage
qImage = ImageQt.ImageQt(pilImage)
# this is a QPixmap
qPixmap = QtGui.QPixmap.fromImage(q)
The other way - Write to disk first
If you follow the example they give on this modules website, they show you how to use saveSnapshot() to save the image to disk. This is less desirable than the first method since you have to do disk i/o, but I will still mention it. You would then read it into your Qt app as a QPixmap:
cam = Device()
cam.saveSnapshot('image.jpg')
qPixmap = QtGui.QPixmap('image.jpg')
Do the first method. Its faster and more efficient.

Categories