15
votes

For my image processing algorithm I'm using python / OpenCV. The output of my algorithm shall be updated im the same window over and over again.

However sometimes the window freezes and doesn't update at all, but the algorithm is still running and updated the picture a multiple times in the meantime. The window turns dark gray on this Ubuntu machine.

Here is an excerpt of the involved code:

for i in range(0,1000):
    img = loadNextImg()
    procImg = processImg(img)
    cv2.imshow("The result", procImg)
    cv2.waitKey(1)

N.B.: processImg() takes about 1-2 s for its procedures. The line cv2.imshow(procImg) creates the window in first instance (i.e. there is no preceding invocation)

9
It's cv2.waitKey that actually pumps the messages for the GUI to work. It needs to be called frequently enough for the UI to actually respond to all the necessary events (like repaints, etc). If the processing takes this long, and you want to have a responsive UI at the same time, you need to do the processing in a separate thread. - Dan Mašek
@DanMašek In a single-threaded program working sequentially I expected first to execute another command, after a current task is completed (in this case update the image). Thanks for the tip with the threading, but to be honest this makes it unnecessary complicated - user3085931
If you don't want to deal with threads (although that's not what i'd consider complicated, but I understand that YMMV), the other possibility is to provide some way to pump the messages (call waitKey) while you're doing the processing (in between the individual steps). It's going to be clumsy, but at least this way the window will remain fairly responsive. - Dan Mašek

9 Answers

12
votes

My suggestion is to use Matplotlib pyplot for displaying the image. I do it the following way.

import matplotlib.pyplot as plt
# load image using cv2....and do processing.
plt.imshow(cv2.cvtColor(image, cv2.BGR2RGB))
# as opencv loads in BGR format by default, we want to show it in RGB.
plt.show()

I know it does not solve the problem of cv2.imshow, but it solves our problem.

8
votes

Increasing the wait time solves this issue. However in my opinion this is unnecessary time spent on sleeping (20 ms / frame), even though it's not much.

Changing

cv2.waitKey(1)

to

cv2.waitKey(20)

prevents the window from freezing in my case. The duration of this required waiting time may vary on different machines.

5
votes

Just add cv2.destroyAllWindows() just after cv2.waitKey()

3
votes

I have the very same issue and I noticed that the fps the window is updated is getting slower and slower until it freezes completely. Increasing the waitKey(x) to something higher just extends the duration where the images are updated but when the time that cv2.imshow() needs to calculate exceeds the time from wait(Key) it just stops updating.

(Skip this complainment:) I think the cv2.imshow() with waitKey() combination is a complete design error, why isn't imshow() just blocking until the UI is updated? That would make life so much easier without having to call waitKey() everytime...

P.S.: There is a possibility to start an own thread for opencv windows inside opencv:

import cv2
img = cv2.imread("image.jpg")
cv2.startWindowThread()
cv2.namedWindow("preview")
cv2.imshow("preview", img)

source: cv2.imshow command doesn't work properly in opencv-python

Well this doesn't work for me because I always get this errors when I run it:

(python3:1177): GLib-GObject-CRITICAL **: g_object_unref: assertion 'G_IS_OBJECT (object)' failed
Attempt to unlock mutex that was not locked
Aborted

Maybe you could try it and report if it is working for you?

Edit: Okay I solved the problem for me by creating a separate script imshow.py:

import cv2
import os.path

while True:
    if os.path.exists("image.pgm"):
        image = cv2.imread("image.pgm")
        if not image is None and len(image) > 0:
            cv2.imshow("Frame", image)
            cv2.waitKey(20)

And I am writing the image out in my other program with: cv2.imwrite("image.pgm", image) And I am calling the script like this:

import subprocess
subprocess.Popen(["python3", "imshow.py"])

Although this is creating some dirty reads sometimes it is sufficient enough for me, a better solution would be to use pipes or queues between the two processes.

2
votes

So what I think is going on here is that the window,(an element of the highGUI) which is still active after the first call to imshow, is waiting for some sort of response from your waitKey function, but is becoming inactive since the program is stuck calculating in either the processImg of loadNextImg functions. If you don't care about a slight waste of efficiency (i.e. you're not running on an embedded system where every operation counts), you should just destroy the window after waitKey, and recreate before imshow. Since the window no longer exists during the time you are processing and loading images, the highGUI wont get stuck waiting for a call from waitKey, and it won't become unresponsive.

1
votes

If your window is going grey then it might be take more processing power. So try to resize image into smaller size image and execute. Sometimes times it freezes while running in ipython notebooks due to pressing any key while performing operation. I had personally executed your problem but I didn't get grey screen while doing it. I did executing directly using terminal. Code and steps are shown below.

import argparse
import cv2
import numpy as np 

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="Path to the image")
args = vars(ap.parse_args())

# load the image, grab its dimensions, and show it
image = cv2.imread(args["image"])
(h, w) = image.shape[:2]
cv2.imshow("Original", image)
cv2.waitKey(0)

for i in range(0,1000):
    image = cv2.imread(args["image"])
    cv2.imshow("The result",image);
    cv2.waitKey(0)

Run it in terminal:

  1. source activate env_name
  2. python Filename.py --image Imagename.png

This will get to your result in one window only(updating each time) without freezing and if you want seperate image in every new window then add .format(i) as given below. But Remember to run in terminal only not in jupyter notebooks.

You can check using terminal commands in this video link https://www.youtube.com/watch?v=8O-FW4Wm10s

for i in range(0,1000):
    image = cv2.imread(args["image"])
    cv2.imshow("The result{}".format(i),image);
    cv2.waitKey(0)

This may help to get you 1000 images separately.

1
votes
try:
    import cv2
except:
    print("You need to install Opencv \n Run this command \n pip install python-opencv")
    exit()
print('Press q to quit frame')
def viewer(name,frame):
    while True:
        cv2.imshow(name,frame)
        if cv2.waitKey(10) & 0xff ==ord('q'):
            break
    return
cv2.destroyWindow(name)

Save this program and from now onwards, import this and use the function viewer to display any frame/image and your display windows will not hang or crash.

0
votes

Add the following two lines of code after cv2.imshow() function,

cv2.waitKey()

cv2.destroyAllWindows()

0
votes

You can use while loop to take burst images without freezing. Here is an example for taking 10 images. You can also try to increase waitkey number and sleep time in while loop. This work for me.

key = cv2.waitKey(1)
webcam = cv2.VideoCapture(0)
sleep(1)

while True:

    try:
        check, frame = webcam.read()
        cv2.imshow("Capturing", frame)
        key = cv2.waitKey(1)

        img_counter = 0

        if key & 0xFF == ord('s'): #press s to take images
            while img_counter < 10:
                check, frame = webcam.read()
                cv2.imshow("Capturing", frame)
                key = cv2.waitKey(1)
                path = 'F:/Projects/' #folder path to save burst images
                img_name = "burst_{}.png".format(img_counter)
                cv2.imwrite(os.path.join(path, img_name), img=frame)
                print("Processing image...")
                img_ = cv2.imread(img_name, cv2.IMREAD_ANYCOLOR) #save as RGB color format
                print("{} written!".format(img_name))
                img_counter += 1
                sleep(0.2)
            webcam.release()
            cv2.destroyAllWindows()
            break

        elif key == ord('q'): #press q to quit without taking images
            webcam.release()
            cv2.destroyAllWindows()
            break

    except(KeyboardInterrupt):
        print("Turning off camera.")
        webcam.release()
        print("Camera off.")
        print("Program ended.")
        cv2.destroyAllWindows()
        break