Hi list
I'm trying to use cv2 to display images created as numpy arrays, from
within a tkinter app (which does other things with the arrays before
they are displayed as images). The arrays are colour-coded
visualisations of genomes and can be over a billion elements in size,
and I've found the PIL methods to display images in tkinter are too
slow and memory-heavy.
Here is minimal code that demonstrates the problem in the subject line:
import cv2
from tkinter import *
images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
counter=[0]
def show():
cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
cv2.waitKey(1)
counter[0] += 1
root=Tk()
root.wm_attributes("-topmost", 1)
Button(root, text=' Show ', command=show).pack()
mainloop()
It works up to a point - I can cycle through the images by clicking the button - but if I mouse-click on the displayed image (e.g. to use the
zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying "'Unknown'
is not responding" and giving the option of waiting or forcing close
(but sometimes these options are greyed out too). Clicking "wait", if available, closes the popup but it comes back a few seconds later. If I
then click on the tkinter window titlebar, the popup changes to "'Tk'
is not responding". Clicking on the button still works and after a few
clicks the popup closes.
This happens under both x11 and wayland, but under wayland, I Â also get
this error:
"QSocketNotifier: Can only be used with threads started with QThread qt.qpa.wayland: Wayland does not support QWindow::requestActivate()"
and only every second button press displays a new image ,with only
every second image displayed.
I think this particular popup is a Gnome thing, but AIUI most DEs have something similar to detect stuck apps. But the app is not stuck.
I suspect this is some kind of interaction between the call to
cv2.waitKey (which is necessary but I've never understood why!) and the tkinter event loop, but it's beyond my knowledge.
Any suggestions about causes or workarounds?
Hi list
I'm trying to use cv2 to display images created as numpy arrays, from
within a tkinter app (which does other things with the arrays before
they are displayed as images). The arrays are colour-coded
visualisations of genomes and can be over a billion elements in size,
and I've found the PIL methods to display images in tkinter are too
slow and memory-heavy.
Here is minimal code that demonstrates the problem in the subject line:
import cv2
from tkinter import *
images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
counter=[0]
def show():
cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
cv2.waitKey(1)
counter[0] += 1
root=Tk()
root.wm_attributes("-topmost", 1)
Button(root, text=' Show ', command=show).pack()
mainloop()
It works up to a point - I can cycle through the images by clicking the button - but if I mouse-click on the displayed image (e.g. to use the
zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying "'Unknown'
is not responding" and giving the option of waiting or forcing close
(but sometimes these options are greyed out too). Clicking "wait", if available, closes the popup but it comes back a few seconds later. If I
then click on the tkinter window titlebar, the popup changes to "'Tk'
is not responding". Clicking on the button still works and after a few
clicks the popup closes.
This happens under both x11 and wayland, but under wayland, I Â also get
this error:
"QSocketNotifier: Can only be used with threads started with QThread qt.qpa.wayland: Wayland does not support QWindow::requestActivate()"
and only every second button press displays a new image ,with only
every second image displayed.
I think this particular popup is a Gnome thing, but AIUI most DEs have something similar to detect stuck apps. But the app is not stuck.
I suspect this is some kind of interaction between the call to
cv2.waitKey (which is necessary but I've never understood why!) and the tkinter event loop, but it's beyond my knowledge.
Any suggestions about causes or workarounds?
Thanks
--
John
On 3/14/2023 6:54 AM, John O'Hagan wrote:
Hi list
I'm trying to use cv2 to display images created as numpy arrays,
from
within a tkinter app (which does other things with the arrays
before
they are displayed as images). The arrays are colour-coded
visualisations of genomes and can be over a billion elements in
size,
and I've found the PIL methods to display images in tkinter are too
slow and memory-heavy.
Here is minimal code that demonstrates the problem in the subject
line:
import cv2
from tkinter import *
images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
counter=[0]
def show():
   cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
   cv2.waitKey(1)
   counter[0] += 1
root=Tk()
root.wm_attributes("-topmost", 1)
Button(root, text=' Show ', command=show).pack()
mainloop()
It works up to a point - I can cycle through the images by clicking
the
button - but if I mouse-click on the displayed image (e.g. to use
the
zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying
"'Unknown'
is not responding" and giving the option of waiting or forcing
close
(but sometimes these options are greyed out too). Clicking "wait",
if
available, closes the popup but it comes back a few seconds later.
If I
then click on the tkinter window titlebar, the popup changes
to "'Tk'
is not responding". Clicking on the button still works and after a
few
clicks the popup closes.
I don't know anything about the specifics here, but with billions of elements you will not be able to show more than a small fraction on
the
screen. Â
So I think that a progressive disclosure approach would pay
off. Sample the arrays down to a more workable size before creating
the
screen images. If you want to zoom in, resample them and recreate
new
images that only cover the zoomed in region in more detail.
It would also be useful to cache the generated images so they can be re-displayed without needing to be regenerated each time.
Assuming you’re using opencv-python, I’d post query at https://github.com/opencv/opencv-python/issues.
On 3/14/23 06:54, John O'Hagan wrote:
Here is minimal code that demonstrates the problem in the subject
line:
import cv2
from tkinter import *
images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
counter=[0]
def show():
   cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
   cv2.waitKey(1)
   counter[0] += 1
root=Tk()
root.wm_attributes("-topmost", 1)
Button(root, text=' Show ', command=show).pack()
mainloop()
It works up to a point - I can cycle through the images by clicking
the
button - but if I mouse-click on the displayed image (e.g. to use
the
zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying
"'Unknown'
is not responding" and giving the option of waiting or forcing
close
(but sometimes these options are greyed out too). Clicking "wait",
if
available, closes the popup but it comes back a few seconds later.
If I
then click on the tkinter window titlebar, the popup changes
to "'Tk'
is not responding". Clicking on the button still works and after a
few
clicks the popup closes.
I think this particular popup is a Gnome thing, but AIUI most DEs
have
something similar to detect stuck apps. But the app is not stuck.
I suspect this is some kind of interaction between the call to
cv2.waitKey (which is necessary but I've never understood why!) and
the
tkinter event loop, but it's beyond my knowledge.
Any suggestions about causes or workarounds?
Thanks
--
John
I don't get any of the zoom/panning behavior with waitKey(1). But a
couple notes from the web:
https://docs.opencv.org/2.4/modules/highgui/doc/user_interface.html
Note
This function should be followed by waitKey function which displays
the
image for specified milliseconds. Otherwise, it won’t display the
image.
For example, waitKey(0) will display the window infinitely until any keypress (it is suitable for image display). waitKey(25) will display
a
frame for 25 ms, after which display will be automatically closed.
(If
you put it in a loop to read videos, it will display the video frame-by-frame)
https://pythonexamples.org/python-opencv-imshow/
cv2.waitKey(0) is important for holding the execution of the python
program at this statement, so that the image window stays visible. If
you do not provide this statement, cv2.imshow() executes in fraction
of
a second and the program closes all the windows it opened, which
makes
it almost impossible to see the image on the window.
if I change waitKey to 0, I get the ability to zoom/pan, but it
places
the tk window in a blocked state because it is waiting on cv2 to
return.
If I hit the ESC key, it releases the wait and gives control back to
the
tk window, allowing me to press show again to continue to the next
image.
I can try to change waitKey to a high ms like 10000000 and have
zoom/pan
for that amount of time before it gives control back to tk (not a
sensical approach).
The fact that you can still see the image after tk takes back control
is
I believe just a matter of design, some examples show cv2.destroyAllWindows() after waitKey(0) to clean that up, but of
course
if you are reusing the same window, that destroys the target.
You can resolve that if you move
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
to inside the show, and destroy it after the wait, and make waitKey
0,
this allows creation/cleanup of that window per image
Hitting ESC when done zooming/panning on each image to get back to
tk.
On Tue, 2023-03-14 at 16:22 -0400, aapost wrote:
On 3/14/23 06:54, John O'Hagan wrote:
It works up to a point - I can cycle through the images by clicking
the
button - but if I mouse-click on the displayed image (e.g. to use
the
zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying
"'Unknown'
is not responding" and giving the option of waiting or forcing
close
(but sometimes these options are greyed out too). Clicking "wait",
if
available, closes the popup but it comes back a few seconds later.
If I
then click on the tkinter window titlebar, the popup changes
to "'Tk'
is not responding". Clicking on the button still works and after a
few
clicks the popup closes.
[...]
I think this particular popup is a Gnome thing, but AIUI most DEs
have
something similar to detect stuck apps. But the app is not stuck.
I suspect this is some kind of interaction between the call to
cv2.waitKey (which is necessary but I've never understood why!) and
the
tkinter event loop, but it's beyond my knowledge.
Any suggestions about causes or workarounds?
Thanks
--
John
I don't get any of the zoom/panning behavior with waitKey(1). But a
couple notes from the web:
https://docs.opencv.org/2.4/modules/highgui/doc/user_interface.html
Note
This function should be followed by waitKey function which displays
the
image for specified milliseconds. Otherwise, it won’t display the
image.
For example, waitKey(0) will display the window infinitely until any
keypress (it is suitable for image display). waitKey(25) will display
a
frame for 25 ms, after which display will be automatically closed.
(If
you put it in a loop to read videos, it will display the video
frame-by-frame)
https://pythonexamples.org/python-opencv-imshow/
cv2.waitKey(0) is important for holding the execution of the python
program at this statement, so that the image window stays visible. If
you do not provide this statement, cv2.imshow() executes in fraction
of
a second and the program closes all the windows it opened, which
makes
it almost impossible to see the image on the window.
if I change waitKey to 0, I get the ability to zoom/pan, but it
places
the tk window in a blocked state because it is waiting on cv2 to
return.
If I hit the ESC key, it releases the wait and gives control back to
the
tk window, allowing me to press show again to continue to the next
image.
I can try to change waitKey to a high ms like 10000000 and have
zoom/pan
for that amount of time before it gives control back to tk (not a
sensical approach).
The fact that you can still see the image after tk takes back control
is
I believe just a matter of design, some examples show
cv2.destroyAllWindows() after waitKey(0) to clean that up, but of
course
if you are reusing the same window, that destroys the target.
You can resolve that if you move
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
to inside the show, and destroy it after the wait, and make waitKey
0,
this allows creation/cleanup of that window per image
Hitting ESC when done zooming/panning on each image to get back to
tk.
Thanks for your reply.
I'm afraid in the real app it won't be practical to have no gui control
while viewing images.
But your suggestions have made me realise that my issue has nothing to
do with tkinter, it seems to be the way imshow is supposed to work.
This code gives the same behaviour described above:
cv2.imshow('W', array)
cv2.waitKey(100)
time.sleep(20)
I've seen the docs you mention that say the window will close if no
keypress happens within the waitKey time, but that doesn't seem to be
what happens as you say. After waitKey returns, the image remains and everything works, but for some reason the desktop complains about an unresponsive app.
You can use cv2 to display video with:
while 1:
frame = camera.read() #pseudocode
cv2.imshow('W', frame)
cv2.waitKey(1)
(In fact this used to work without waitKey if you called cv2.startWindowThread() after creating the window, but that stopped
working at some point.)
The fact that this works, presumably because the image is being
replaced often enough, suggested the following workaround to my
problem, using a call to after() to re-display the current image before
the desktop starts complaining:
images=[cv2.imread(i) for i in ('a.jpg', 'b.jpg', 'c.jpg')]
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
counter=[0]
image = [None]
def show():
im = images[counter[0] % 3]
cv2.imshow('W', im)
cv2.waitKey(1)
image[:] = [im]
counter[0] += 1
root=Tk()
root.wm_attributes("-topmost", 1)
b=Button(root, text=' Show ', command=show)
b.pack()
def update():
if image[0] is not None:
cv2.imshow('W', image[0])
cv2.waitKey(1)
root.after(1000, update)
update()
mainloop()
Not ideal, but I'm starting to think that despite the "highgui" label, cv2.imshow is not really suited for integration into a gui. Which is a
shame because it's so damn fast!
On Tue, 2023-03-14 at 16:22 -0400, aapost wrote:
On 3/14/23 06:54, John O'Hagan wrote:
On Tue, 2023-03-14 at 16:22 -0400, aapost wrote:
On 3/14/23 06:54, John O'Hagan wrote:
[...]
For example, waitKey(0) will display the window infinitely until anykeypress (it is suitable for image display). waitKey(25) will display a
Note: This function should be followed by a call to cv::waitKey or cv::pollKey to perform GUI housekeeping tasks that are necessary toactually show the given image and make the window respond to mouse and
On 3/15/23 07:37, John O'Hagan wrote:
On Tue, 2023-03-14 at 16:22 -0400, aapost wrote:
On 3/14/23 06:54, John O'Hagan wrote:
[...]
Read an alternative description of the waitKey behavior
;For example, waitKey(0) will display the window infinitely untilany
keypress (it is suitable for image display). waitKey(25) will display
a
frame and wait approximately 25 ms for a key press (suitable for
displaying a video frame-by-frame). To remove the window, use cv::destroyWindow.
I went back to double check and I stand corrected on the "any
keypress"
part. Any keypress on the 'keyboard' does break the wait (as I
incorrectly concluded and assumed only ESC did).
It is still slightly ambiguous in explaining that when using 25ms,
keypress or not, the wait breaks at 25ms (or before that if you press
a
keyboard key). For my setup the window is stale at that point, no
controls, just a stale frame behind the tkinter window that needs to
be
destroyed or reused.
Whether that is the exact behavior on all set-ups isn't clear, the
note
further uses the ambiguous phrasing "might".
;Note: This function should be followed by a call to cv::waitKey or cv::pollKey to perform GUI housekeeping tasks that are necessary toactually show the given image and make the window respond to mouse
and
keyboard events. Otherwise, it won’t display the image and the window
might lock up.
It seems with the several variations, behavior varies between them,
like
one comment saying startWindowThread when using c++ and gtk allows
you
to not use waitKey (not the case here.. it seems -- changing my
language
to not misspeak, lol).
I haven't come across any examples beyond imshow running stand-alone
as
in my solution suggestion 2. But waitKey does return a keyvalue for
the
keypess to allow extending the functionality, so you can grab it, do something, and go right back to waiting, I haven't seen any use of
this
though.
You can also leave out reference to tkinter all together when using startWindowThread:
import sys
import cv2
cv2.startWindowThread()
cv2.namedWindow("W", cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty("W", cv2.WND_PROP_FULLSCREEN,
cv2.WINDOW_FULLSCREEN)
cv2.imshow("W", cv2.imread(sys.argv[1]))
while(1):
    a = cv2.waitKey(0)
    if a == 27:#ESC
        break
    #elif a == something else, do something
cv2.destroyAllWindows()
exit()
But it still blocks if integrated in to the main tkinter thread, and
appears to use tkinter under the hood. And as tkinter is considered thread-unsafe, the startWindowThread would only be ok when spawned as
a
separate process like the subprocess example.
Here is minimal code that demonstrates the problem in the subject
line:
import cv2
from tkinter import *
images=['a.jpg', 'b.jpg', 'c.jpg'] #change to image paths
cv2.namedWindow('W', cv2.WND_PROP_FULLSCREEN)
cv2.setWindowProperty('W', cv2.WND_PROP_FULLSCREEN,Â
cv2.WINDOW_FULLSCREEN)
counter=[0]
def show():
cv2.imshow('W', cv2.imread(images[counter[0] % len(images)]))
cv2.waitKey(1)
counter[0] += 1
root=Tk()
root.wm_attributes("-topmost", 1)
Button(root, text=' Show ', command=show).pack()
mainloop()
It works up to a point - I can cycle through the images by clicking
the button - but if I mouse-click on the displayed image (e.g. to use
the zooming and panning features of cv2), nothing happens, and a few
seconds later the image greys out and a popup appears saying
"'Unknown' is not responding" and giving the option of waiting or
forcing close (but sometimes these options are greyed out too).
Clicking "wait", if available, closes the popup but it comes back a
few seconds later. If I then click on the tkinter window titlebar,
the popup changes to "'Tk' is not responding". Clicking on the button
still works and after a few clicks the popup closes.
--
John
Sysop: | Keyop |
---|---|
Location: | Huddersfield, West Yorkshire, UK |
Users: | 300 |
Nodes: | 16 (2 / 14) |
Uptime: | 74:48:54 |
Calls: | 6,715 |
Calls today: | 3 |
Files: | 12,246 |
Messages: | 5,357,277 |