Imagemagick's import is frustratingly slow - linux

I am writing an small python script to take screenshots of a game window (which will be in the background/minimized) and performing some simple template matching and ocr using cv2.
I am currently calling im's import as follows:
import -window windowID png:-
to take a screenshot of an inactive window.
However this takes almost 4s and is easily the slowest part of my script by a factor of 100x.
Is there any alternative to import or perhaps another way of approaching this that will be faster?
I have already tried graphicsmagick (ended up being slower than imagemagick) and xwd (did not capture the unfocused window even though the windowID was specified)
Link to full python script (Line 44 is where the screenshot taking happens)

You are doing all the PNG encoding and zlib compression in ImageMagick and then decompressing it all again in OpenCV. I guess you would do better if you found a format that is more closely shared between the two.
Specifically, ImageMagick could give you RGB pixels directly, which you could then convert to BGR very easily in OpenCV with cvtColor().
import -window windowID rgb:
You would have to query the window dimensions to get width and height.
Alternatively, you could use PPM format which OpenCV can also read without any libraries and which includes dimensions:
import -window windowID ppm:

Related

How to save pygame window as a image? (python3) (PyGame)

I'm making a pixel editor / a trash version of ms paint in python with pygame, and I want to save the window (canvas?) as a png or jpg. I've seen pygame.image.save, but that only saves a certain surface as an image, I want to save the entire window.
Give the following a try:
pygame.image.save(window, "screenshot.png")
Use pygame.image.save(), which requires PyGame 1.8 or later. If you give it the base-level window surface, it will indeed save the entire window content.
For example:
pygame.image.save( window, 'surface.png' )
The image type is determined by the filename suffix.

Does the backend matter for savefig

I have a script which plots some pandas data, and then either shows the plot interactively with plt.show(), or saves it to a file with plt.savefig(args.out).
import matplotlib.pyplot as plt
# set up the dataframe here
ax = df.plot.line(x=0, title=args.title, figsize=(12,8), grid=True, **kwargs)
if (args.out):
vprint("Saving figure to ", args.out, "...")
plt.savefig(args.out)
else:
vprint("Showing interactive plot...")
plt.show()
The question is, does the default matplotlib backend matter for the scenario where I save to a file with savefig? It definitely matters in the other case since it's used to display the interactive plot, but if I call savefig is another backend used entirely?
When showing a figure, the backend obviously matters, because it provides two things:
The renderer to draw the image
The GUI within which the image is shown.
When saving a figure, only the former matters. However, matplotlib provides a multitude of export formats. At the end, the chosen backend will determine what to do when a figure is saved, and in most cases, will use one of the existing non-interactive backends to produce the output file.
Some examples:
TkAgg will use the tkinter GUI to show a figure. For saving a png figure, it will fall back to the basic Agg backend to produce the png file. For saving an svg file, it will fall back to the svg backend, for saving a pdf it will fallback to the pdf backend, etc.
TkCairo, will use the tkinter GUI to show a figure. For saving a png figure, it will fall back to the basic Cairo backend to produce the png file. For the rest, same as above.
Qt5Agg will use the PyQt GUI to show a figure. For png will fall back to Agg. For others same as above.
similar for other backends.

PIL -> PyGame image conversion: Partial data loss

I'm using the C-based screenshot concept from JHolta's answer in Take a screenshot via a python script. [Linux] in order to generate screenshots I'd like to display in PyGame. With some minor tweaks (prepending extern "C" to the functions and importing Xutil instead of Xlib) the provided code works amazingly well. In short, it uses Image.frombuffer on a byte array returned by the C library. With show(), the image and anything about it I manipulate is displayed by ImageMagick.
However, if I convert it to Python 3's PyGame as per PIL and pygame.image, I only get a black surface. It's not a straightforward issue, though: If I draw onto the image before converting it into a PyGame image (like in the OP of the latter link), that does show on a black background when blitting the result. Furthermore, printing the byte objects from PILImage.tobytes and pygame.image.tostring shows they both contain data and their len is identical.
What am I doing wrong here? I'll gladly provide code if necessary, but I think it's more of a conceptual issue and I didn't really change the snippets from these answers a lot.
(Similar issue in Python 2, by the way, but there PyGame uses str instead of byte for tostring / fromstring and printing the tostring appears to yield an empty string.)
It turns out that a buggy trigger caused the screenshoot to be taken again while the fullscreen window displaying it was opening. I suppose there are a few milliseconds of blackness or of an undefined state (in the context of the screenshot function) at that moment, and the library is fast enough to catch that.
I'm not sure if this should stay up because it's basically a reminder to check for things that a human can't perceive. Feel free to delete if it's not appropriate.

How do you create a perfect pygame fullscreen?

I am making a game, and I want it to be fullscreen. However, the pygame fullscreen is strange, creating a screen too large. So I referred to this: Pygame FULLSCREEN Display Flag Creates A Game Screen That Is Too Large For The Screen. However, when I followed these instructions
import ctypes
ctypes.windll.user32.SetProcessDPIAware()
true_res = (ctypes.windll.user32.GetSystemMetrics(0), ctypes.windll.user32.GetSystemMetrics(1))
pygame.display.set_mode(true_res,pygame.FULLSCREEN)
from an answer (but instead using pywin32 instead of ctypes, like this: win32api.GetSystemMetric(0)).
I used this, and while it does create a fullscreen, it also creates a black border around my screen and enlarges everything a slight bit, including my cursor. How can I get rid of this black border and get all shapes to normal size? Or is there a better way to create a good fullscreen?
If it helps, I use Windows 10.
Thanks in advance!
I think the problem of enlarging everything arose with the use of ctypes module as because the ctypes module makes use of a function named as GetSystemMetrics() whose work is to get the size of the screen of your system.
And might be the import pygame is loading some dll that is not compatible with a dll that windll needs.
So I suggest either you update the ctype library or pygame library or update both libraries or you can enlarge screen size by providing custom width and height values according to the resolution supported by your system.
Hope this helps !!

How to get Numpy array of Canvas data?

I am building an application with Tkinter, where one is able to draw e.g. lines in a Canvas. This works well. However, I'm unable to find a method for getting the current Canvas data. Preferably I would like to get a numpy array out of the current Canvas data, since my post-processing steps are mostly using numpy.
Is there any way to build numpy arrays out of the Canvas data? In some color format like RGB, by preference?
I know that I can get the information e.g. of lines (like coordinates) out of the Canvas, but I do not need this information. I need a rasterized image data of the whole Canvas scene. Like a numpy array or a (rasterized) image (jpg, png, tiff, bitmap, ...).
Like #Bryan Oakley said: there is no way to get a rasterized version of a Tkinter Canvas drawing.
However, I figured out this workaround:
import skimage.io as ski_io
(...)
# draw your canvas
(...)
# save canvas to .eps (postscript) file
canvas.postscript(file="tmp_canvas.eps",
colormode="color",
width=CANVAS_WIDTH,
height=CANVAS_HEIGHT,
pagewidth=CANVAS_WIDTH-1,
pageheight=CANVAS_HEIGHT-1)
# read the postscript data
data = ski_io.imread("tmp_canvas.eps")
# write a rasterized png file
ski_io.imsave("canvas_image.png", data)
I do not really like workarounds, but skimage seems to be the fastest solution for reading postscript files and writing pngs.
Scikit-image is developed as a toolkit for SciPy, therefore it is working with scipy.ndimage internally, which is exactly what I want and can be used to create np.ndarray very easily.
Additionally scikit-learn is a powerful and fast image processing software itself, which can manipulate, read, and save various image formats.
Now you have the full choice: get a NumPy np.ndarray out of Canvas data for further computations, manipulate the scipy.ndimage with SciPy/scikit-image or save the data, e.g. as a rasterized png, to disk.

Resources