Opencv在更新时,imshow()函数会冻结

20

我正在使用Python/OpenCV进行图像处理算法。我的算法输出应该在同一个窗口中一遍又一遍地更新。

但是有时窗口会冻结并且根本不更新,但算法仍在运行,并在此期间多次更新图片。在这台Ubuntu机器上,窗口变成了深灰色。

以下是涉及的代码摘录:

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

注意: processImg() 执行其过程需要大约1-2秒的时间。行 cv2.imshow(procImg) 在第一次调用时创建窗口(即没有之前的调用)。


3
实际上是 cv2.waitKey 推送消息以使 GUI 正常工作。需要频繁调用它,以便 UI 能够响应所有必要的事件(例如重绘等)。如果处理时间过长,并且您同时希望拥有响应灵敏的 UI,则需要在单独的线程中进行处理。 - Dan Mašek
@DanMašek 在一个单线程程序中,我期望在当前任务完成后(在这种情况下更新图像),首先执行另一个命令。感谢您提供关于线程的提示,但老实说,这使得它变得不必要地复杂。 - user3085931
2
如果您不想处理线程(虽然我认为这并不复杂,但我理解您的情况可能有所不同),另一种可能性是在处理过程中提供一些方式来传递消息(调用waitKey)。这样做会很笨拙,但至少这样窗口仍然能够保持相当灵敏。 - Dan Mašek
10个回答

19

我建议使用Matplotlib pyplot来显示图像。我是这样做的。

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()

我知道这并没有解决cv2.imshow的问题,但它解决了我们的问题。


2
cv2 的版本 4.1.2 中(通过 opencv-python 包含),似乎你传递给 cvtColor 的标志已经从 cv2.BGR2RGB 更改为 cv2.COLOR_BGR2RGB - blthayer
这只是一个解决方法的建议,而不是真正的解决方案。我更喜欢@MohanavelT在其他答案中提供的解决方案-目前在下面。 - MaciekS

10

增加等待时间可以解决此问题。但是在我看来,这是不必要的睡眠时间(每帧20毫秒),尽管不多。

更改

cv2.waitKey(1)

to

cv2.waitKey(20)

在我的情况下,它可以防止窗口冻结。这个必要等待时间的持续时间可能因不同的机器而异。


4
只需在 cv2.waitKey() 后面添加 cv2.destroyAllWindows() 即可。

我建议将与imshow相关的代码放入try-except-finally语句中,并将cv2.destoryAllWindows()放入finally子句中,以便在发生异常情况下也能销毁窗口。 - MaciekS

3
我有相同的问题,我注意到窗口更新的帧率越来越慢,直到完全冻结。增加waitKey(x)的时间只能延长图像更新的时间,但是当cv2.imshow()需要计算的时间超过wait(Key)的时间时,它就会停止更新。
(略过这个抱怨:) 我认为cv2.imshow()和waitKey()的组合是一个完全的设计错误,为什么imshow()不仅仅是阻塞,直到UI更新呢?这将使生活变得更加轻松,而不必每次都调用waitKey()......

P.S.:在opencv内部有可能启动一个独立的线程来运行opencv窗口:

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

来源:cv2.imshow命令在opencv-python中无法正常工作

对我来说,这并不起作用,因为每次运行时都会出现以下错误:

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

也许你可以尝试它并报告一下它是否适用于你?

编辑: 好吧,我通过创建一个单独的脚本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)

我在另一个程序中使用以下代码将图像写入文件:cv2.imwrite("image.pgm", image) 我以以下方式调用脚本:

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

尽管有时会产生一些脏读,但对我来说已经足够了。更好的解决方案是在两个进程之间使用管道或队列。

但是你又一次在处理一个20毫秒的循环,不是吗? - user3085931
目前,我不知道有什么方法可以正确地摆脱那个waitkey函数。但是下一步我将尝试用另一个非opencv的解决方案来替换opencv imshow以显示图像。你知道这方面的任何东西吗? - ElDani
那么我就无法获得这篇文章的信息增益。建议查看Python中的TkinterPIL - user3085931
但为什么不呢?额外的进程解决了冻结的问题,这正是这个线程所讨论的吗?无论如何,还是谢谢你的信息。 - ElDani
当我将等待时间增加到20毫秒时,我也能够获得相同的结果,这意味着创建额外的线程是不必要的开销,在几个月后,没有人会再理解它的目的(根据我的经验)。 - user3085931
1
那对我来说没用,但额外的步骤确实有效,所以把这篇帖子留在原地肯定是有意义的。 - ElDani

2
我认为这里发生的情况是,窗口(高级GUI的一个元素)在第一次调用imshow后仍然处于活动状态,正在等待waitKey函数的某种响应,但由于程序在processImg或loadNextImg函数中被卡住计算,所以它变得不活跃。 如果您不关心轻微浪费效率(即您不在嵌入式系统上运行,其中每个操作都很重要),您应该在waitKey之后销毁窗口,并在imshow之前重新创建窗口。 由于窗口在您处理和加载图像的时间段内不存在,因此高级GUI不会被卡住等待waitKey的调用,也不会变得无响应。

我尝试在更新之前(或在这种情况下再次创建新窗口)关闭窗口。结果是,它不会变成灰色,而是保持空白白色。这个解决方案的另一个问题是:窗口会在随机位置生成,如果我想移动它,在下一次更新后,新窗口会再次创建在旧位置。这可能会妨碍剩余的工作。 - user3085931
要解决窗口随机出现的问题,只需在创建窗口后立即调用moveWindow,并指定所需窗口的x、y位置。此外,窗口保持空白白色表明它仍然处于活动状态,只是您可能正在传递一个白色图像进行显示。此时,我建议检查算法本身。一种好的方法是将要显示的图像写入文件,然后查看该文件。如果文件全部为白色,则是算法问题。 - bstadt
算法没问题,我只是增加了工作量,之前它运行得很好——我的意思是在Ubuntu中,当一个窗口变成灰色时,意味着这个应用程序没有响应。由于这是一个顺序程序例程,当更新时,新图像已经完成——实际上,当新图像显示时,它首先开始处理下一帧。这就是为什么我认为错误出在OpenCV方面。换句话说,OpenCV似乎会在返回时自行启动一些线程,但甚至还没有完成。 - user3085931

1
如果您的窗口变灰,那么可能需要更多的处理能力。尝试将图像调整为较小的尺寸并执行。有时在运行 ipython 笔记本时会冻结,这是由于在执行操作时按下任何键。我已经亲自执行过您的问题,但在执行时没有出现灰色屏幕。我直接使用终端执行了代码和步骤如下所示。
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)

在终端中运行:

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

这将在一个窗口中得到您的结果(每次更新)而不会冻结,如果您想在每个新窗口中获得单独的图像,则添加.format(i),如下所示。但是请记住仅在终端中运行,而不是在jupyter笔记本中运行

您可以使用此视频链接中的终端命令进行检查 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)

这可能有助于将1000张图片分开获取。


1

这是一个旧的帖子,但如果有其他人遇到了同样的问题,通常是在使用pip更新opencv/opencv-contrib版本时出现的,但仍然存在一些未满足的依赖关系(例如您可能已经安装了numpy,因此它不会重新安装它,这会导致它在后台崩溃)。

只需执行以下操作:

pip install opencv-python opencv-contrib-python --no-cache --force-reinstall

版本4.5.2.52在Ubuntu 20.04和18.04上与Python > 3.8正常工作。


1
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)

保存此程序,从现在开始导入它并使用函数查看器来显示任何帧/图像,您的显示窗口将不会挂起或崩溃。


0
cv2.imshow()函数之后添加以下两行代码。

cv2.waitKey()

cv2.destroyAllWindows()


1
重复的 @Mohanavel - user3085931

0

您可以使用while循环拍摄连续图像而不会冻结。以下是拍摄10张图像的示例。您还可以尝试增加while循环中的waitkey数字和sleep时间。这对我有用。

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接