Optimize the process of importing multiple images from the internet, tkinter - python

I'm writing a recipe program and as part of it, I want to make a series of buttons with different images (for each recipe). I am using the Edamam recipe search api (here) which gives me urls to all the images I need.
The way I my code operates is as follows:
for each recipe:
data = urlopen(image_url)
image = ImageTk.PhotoImage(data=data.read()) # Convert to a tkinter PhotoImage object
b = Button(root, command = lambda i=recipe_url:callback(i), image = image) # callback() opens the webpage for a given url
b.image = image # To prevent garbage collection from removing the image
b.pack()
Obviously this is a simplified version and I've removed or renamed things for it to make sense without looking at the entire program. However, it addresses the performance issue which is the process of downloading and converting images.
The relevant information I can think of is:
20 images need to be converted each time
It takes around 18 seconds for the entire process to run
I am using the smallest sized images available (sizes of large, medium, small, and thumbnail are available)
The relevant modules I'm using are PIL, urllib, and tkinter
My question is how can I increase the speed of this component without compromising on functionality.
Let me know if there's any other information I need to add and thank you in advance for the help.

Thanks to #Alexander for the tip off on multiprocessing, I tried implementing it but I couldn't really wrap my head around it, so I used multithreading instead and now it is far more efficient. Each thread contains the code for one button and then the loop simply defines and then starts the threads:
x = threading.Thread(target=image_conversion, args=(item,))
x.start()

Related

Cycled images in linux/gtk program suddenly stop displaying

I have an extremely simple application for a Raspberry Pi. (It's an educational kiosk for a children's museum if anyone cares.) In python, I have an infinite loop in a thread reading a line from a serial port. Based on the input, I display one of 14 different jpg images. I am not putting all the code here, but it's a very bare-bones GDK application. I have an Arduino feeding the serial port the information to cycle through all the images for debug purposes. In response to the input, I do the following:
self.CurrentImage.set_from_file("image.jpg") # the name here is one of 14
Not to anyone's great surprise, this works. But as I let the Arduino hammer at the input, the screen would randomly show a white image and nothing again after. I checked the standard out window and the data was still coming and the images still being read. And when I say random I mean that at some point in the input-and-display process, it stops displaying. There are no errors being reported. Sometimes I might get 4-5 images in sequence before it dies, or I might make it through the list twice. It's simply not deterministic. My mind wandered to thinking maybe I'm not clearing first and having a memory leak. I made the following amendment:
self.CurrentImage.clear()
self.CurrentImage.set_from_file("image.jpg")
The problem persisted. I decided to scrap the method and go for something that didn't involve reloading images. At startup I created a separate GTK Image widget for each file. Then in response to the input data, I did this:
self.CurrentImage.hide()
self.CurrentImage = self.AlphaImage # or one of the other 13 Images I created
self.CurrentImage.show()
The nice thing about this method is that the image displays much faster. The first method had the screen briefly go white as the image was loaded. However, once again, after a random number of image switches, the window goes white. Diagnostic output shows that the loop is happily reading data and selecting images.
In the original version where I loaded images as needed, there was exactly one widget on the window. So it's not possible that another widget is covering it. The second version has an Image widget for each jpg file. If one is covering another, I should still at least see that image.
I'm good at thinking outside the box, but I admit that Linux is a weak area for me. Nothing is occurring to me to try to make this work. I'd whinge that I'm under time pressure here and children will be disappointed... but it was supposed to be done before Christmas and I only got the final art yesterday. That reminds me that there's one final note and the reason I thought my first method was failing: I created temporary graphics of my own that was one word of black text on a white background. Those images displayed without problem until the screensaver kicked in.
I'm open to any suggestion as to how to track this down and fix it.
Thanks to Sylvester, I figured this one out. The problem isn't how I was updating the images, it was where I was doing it. In the thread catching the serial input was not the place to do it. I reduced the thread to simply reading the line, then did the following:
GLib.idle_add( self.updateImage, lineInput )
then in the function updateImage I did the business logic of selecting the correct image and updating. Problem solved.

ImageItem crashing after a minute of displaying frames from camera

I have a PyQtGraph widget that I am using to display processed arrays from a framegrabber. A thread acquires these puts these data into a queue and another thread gets these data from the queue and calls update(data) on my widget. Data is a relatively small (400*100) numpy array
class BScanView(PyQtG.GraphicsLayoutWidget):
def __init__(self, aspect=0.5):
super().__init__()
self.aspect = aspect
self.viewbox = self.addViewBox(row=1,col=1)
self.viewbox.setAspectLocked()
self.image = PyQtG.ImageItem()
self.viewbox.addItem(self.image)
def update(self, data):
self.image.clear()
self.image.setImage(data, autoLevels=False, levels=(-100, -2))
QtGui.QGuiApplication.processEvents()
This works for awhile but randomly crashes the ImageItem. The rest of the GUI works fine for subsequent use but the above widget is unresponsive.
It's difficult to say because I cannot reproduce your issue but I can think of a few potential causes.
Perhaps the underlying data buffer of the data array is shared between the threads and updated incorrectly. Try making a copy of the array before you set it in the image with self.image.setImage(np.copy(data),...
The GraphicsLayoutWidget is a decendent of QWidget and therefore has an update method. You override it with a different signature. I don't know PyQt handles this exactly but try renaming your method to updateImage and see if it makes a difference.
Why are you using processEvents? You should not need it here.
Your code example is not an MVCE in the sense that it's not complete; information is missing on how the data is created in the other thread. Hundred percent completeness is not always achievable (external libraries and such), but try to make it as complete as possible. To make a complete, minimal and verifiable example can be a fair amount of work, but the more effort you put into it, the more answers you will receive and the better they will be.

Is it possible to save in a file an animation created with Tkinter?

I wanted to use Python to create animations (video) containing text and simple moving geometric objects (lines, rectangles, circles and so on).
In the book titled "Python 2.6 Graphics Cookbook" I found examples using Tkinter library. First, it looked like what I need. I was able to create simple animation but then I realized that in the end I want to have a file containing my animation (in gif or mp4 format). However, what I have, is an application with GUI running on my computer and showing me my animation.
Is there a simple way to save the animation that I see in my GUI in a file?
There is no simple way.
The question Programmatically generate video or animated GIF in Python? has answers related strictly to creating these files with python (ie: it doesn't mention tkinter).
The question How can I convert canvas content to an image? has answers related to saving the canvas as an image
You might be able to take the best answers from those two questions and combine them into a single program.
I've accomplished this before, but not in a particularly pretty way.
Tl;dr save your canvas as an image at each step of the iteration, use external tools to convert from image to gif
This won't require any external dependencies or new packages except having imagemagick already installed on your machine
Save the image
I assume that you're using a Tkinter canvas object. If you're posting actual images to the tk widgets, it will probably be much easier to save them; the tk canvas doesn't have a built-in save function except as postcript. Postscript might actually be fine for making the animation, but otherwise you can
Concurrently draw in PIL and save the PIL image https://www.daniweb.com/software-development/python/code/216929/saving-a-tkinter-canvas-drawing-python
Take a screenshot at every step, maybe using imagegrab http://effbot.org/imagingbook/imagegrab.htm
Converting the images to to an animation
Once the images are saved, I used imagemagick to dump them into either a gif, or into a mpg. You can run the command right from python using How to run imagemagick in the background from python or something similar. It also means that the process is implictely run on a separate thread, so it won't halt your program while it happens. You can query the file to find out when the process is done.
The command
convert ../location/*.ps -quality 100 ../location/animation.gif
should do the trick.
Quirks:
There are some small details, and the process isn't perfect. Imagemagick reads files in order, so you'll need to save the files so that alphabetical and chronological line up. Beware that the name
name9.ps
Is alphabetically greater than
name10.ps
From imagemagick's point of view.
If you don't have imagemagick, you can download it easily (its a super useful command-line tool to have) on linux and mac, and cygwin comes with it on windows. If you're worried about portability... well... PIL isn't standard either
There is a way of doing that, with the "recording screen method", this was explained in other question: "how can you record your screen in a gif?".
Click the link -->LICEcap : https://github.com/lepht/licecap
They say that it's free software for Mac (OS X) and Windows
You could look at Panda3D, but it could be a little over killed for what you need.
I would say you can use Blender3d too but i'm not really sure of how it works. Someone more experimented then me could tell you more about this.

How to capture the screen output of a running program?

Is it possible to take screenshots of a running program (with GUI) from another python program ?
If so, what could be the steps and libraries that I could use ? (On Windows)
For example, let's say I have calc.exe running. I'd want to take screenshots of what is displayed to the user from myprogram.py.
My goal is to analyze what's displayed on the monitored program.
If it's not possible to isolate the screenshot to a running predefined program, I think I will have to take screenshots of the fullscreen but it's not very practical.
Capturing an screenshot is easy. Just install the Python Imaging Library and use the ImageGrab.grab() function to return an Image instance with the screenshot.
Capturing an specified window is a little more complicated, because you need the window coordinates. I recommend you to install the win32api modules and use a little module called winGuiAuto.py. Once you do that, you can do something like this:
hwnd = winGuiAuto.findTopWindow(title)
rect = win32gui.GetWindowPlacement(hwnd)[-1]
image = ImageGrab.grab(rect)
However, capturing the screen is the easy part. If you want to analyze the contents from screenshots, you're in for a lot of complications. This is probably the wrong approach for doing what you want and should be left as a last resort.
In most cases, it's easier to use the windows api to read the contents of a window's elements directly, but that won't work with some 3rd party GUI toolkits. That's not within the scope of your question so I'm not detailing it here, but you should read the source of the winGuiAuto.py module mentioned above for examples on how to do that, as well as checking the pywinauto library.
The ImageGrab Module, works on Windows only. The pyscreenshot module, is a better replacement for that, can be used to copy the contents of the screen to a PIL or Pillow image memory. Read more at link below.
https://pypi.python.org/pypi/pyscreenshot

Python-spawned subprocess jpgs in new window and exit on completion

I have a file containing thus far some 1800 jpegs (it will grow) which I have to search manually for specific features. This requires that I enter a code (1 or 0) to say whether or not the features that I am looking for exist.
Because the features are difficult to define, the images of low quality and as far as we are aware no non-deterministic approach exists to deal with this satisfactorily it has to be coded.
The idea is to open the large_file containing the jpegs, open each of them in a new window one-by-one (so that I can see them), read in data from the screen and then close the new window.
Roughly this looks something like this:
large_file = open (xxxxxx, rb) # contains info identifying jpeg
for jpeg in large_file: # 1800 items now more later
open jpeg in separate window
does image meet the criterion?: # Enter 1 or 0 (say) at console
if (1 or 0):
close window and move to next image
else:
go back and ask for sensible input
large_file.close()
I am running Kubuntu 14.04 with lightdm as the display manager and Python 2.7.6. Browsing Stackoverflow doesn't really get me what I need.
Implementing using Image cause problems because I am not using xv, but lightdm. Several strategies have not worked properly. Clearly the core issue is spawning the new window and then closing it.
Any suggestions? In an ideal world I would prefer not to have to close images manually...
Many thanks

Categories