【问题标题】:openCV doesn't always actualize the display/show the imageopenCV 并不总是实现显示/显示图像
【发布时间】:2021-03-20 17:49:00
【问题描述】:

我正在使用cv2.imshow() 和cv2.waitKey() 显示一些非常简单的带有openCV 的numpy 数组。有时,我希望显示停留几秒钟,然后恢复我的程序。我只需添加一个带有所需睡眠值的time.sleep()。大多数时候,一切正常。但是,有时显示不会改变并保持在前一个显示。这是非常不一致的。相同的代码可能在 80% 的时间内有效,而在剩下的 20% 中可能无效。有趣的是,它似乎总是相同的图像/显示在多次运行中不起作用;尽管有时它也确实有效。正如我所说,它非常不一致。

这是我用来创建和显示显示的一小部分代码:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import sys
import numpy as np
import cv2
import time

from matplotlib import colors

class Visual:
    def __init__(self, window, screen_size=None):
        self.window = window
        # screen size and message setting
        if screen_size is None:
            if sys.platform.startswith('win'):
                from win32api import GetSystemMetrics
                self.screen_width = GetSystemMetrics(0)
                self.screen_height = GetSystemMetrics(1)
            else:
                self.screen_width = 1024
                self.screen_height = 768
        else:
            self.screen_width, self.screen_height = screen_size
        self.screen_center = (self.screen_width//2, self.screen_height//2)
        
    def draw_background(self, color=(210, 210, 210)):
        if isinstance(color, str):
            r, g, b, _ = colors.to_rgba(color)
            color = [int(c*255) for c in (b, g, r)]
            
        self.img = np.full((self.screen_height, self.screen_width, 3), 
                           fill_value=color, dtype=np.uint8)
        
    def show(self, wait=1):
        cv2.imshow(self.window, self.img)
        cv2.waitKey(wait)
        
class CrossVisual(Visual):
    def __init__(self, window, screen_size=None):
        super().__init__(window, screen_size)
    
    def draw_cross(self, width_ratio=0.2, height_ratio=0.02, position='center', color='black'):
        if isinstance(color, str):
            r, g, b, _ = colors.to_rgba(color)
            color = [int(c*255) for c in (b, g, r)]
        
        rect_width = int(width_ratio*self.screen_width)
        rect_height = int(height_ratio*self.screen_height)
        
        if position == 'center':
            x = self.screen_center[0]
            y = self.screen_center[1]
        else:
            x, y = position
        
        # Rectangle 1
        xP1 = x - rect_width//2
        yP1 = y + rect_height//2
        xP2 = x + rect_width//2
        yP2 = y - rect_height//2
        cv2.rectangle(self.img, (xP1, yP1), (xP2, yP2), color, -1)
        
        # Rectangle 2
        xP1 = x - rect_height//2
        yP1 = y + rect_width//2
        xP2 = x + rect_height//2
        yP2 = y - rect_width//2
        cv2.rectangle(self.img, (xP1, yP1), (xP2, yP2), color, -1)
        
class TextVisual(Visual):
    def __init__(self, window, screen_size=None):
        super().__init__(window, screen_size)
        
    def putText(self, text, fontFace=cv2.FONT_HERSHEY_DUPLEX, 
                fontScale=2, color='white', thickness=2, vertical_offset=0):
        if isinstance(color, str):
            r, g, b, _ = colors.to_rgba(color)
            color = [int(c*255) for c in (b, g, r)]
        
        # Measuring text and choosing position on the screen
        textWidth, textHeight = cv2.getTextSize(text, fontFace, fontScale, thickness)[0]
        xtext = int(self.screen_center[0] - textWidth//2)
        ytext = int(self.screen_center[1] - textHeight//2 + vertical_offset)
        
        # print warnings
        if textWidth > self.screen_width or textHeight > (self.screen_height - ytext):
            print ('WARNING: The text and text settings lead to an output too large for the screen size.')
            
        cv2.putText(self.img, text=text, org=(xtext, ytext),
                    fontFace=fontFace, fontScale=fontScale, 
                    color=color, thickness=thickness, lineType=cv2.LINE_AA)

很遗憾,我无法用一小段代码重现该问题。但我可以用以下函数演示:

def display_3s_countdown(window, images=None):
    if images is None:
        images = list()
        
        Visual = TextVisual(window=WINDOW)
        Visual.draw_background(color='lightgrey')
        Visual.putText('3', color='black')
        images.append(Visual.img)
        
        Visual = TextVisual(window=WINDOW)
        Visual.draw_background(color='lightgrey')
        Visual.putText('2', color='black')
        images.append(Visual.img)
        
        Visual = TextVisual(window=WINDOW)
        Visual.draw_background(color='lightgrey')
        Visual.putText('1', color='black')
        images.append(Visual.img)
    
    for image in images:
        cv2.imshow(window, image)
        cv2.waitKey(1)
        time.sleep(1)

如您所见,这个小函数只是创建了 3 张图像,每张图像都写有不同的数字,以创建一个 3 秒的小倒计时。当我做类似的事情时:

v = TextVisual('test window')
v.draw_background(color='lightgrey')
v.putText('some random text', color='black')
time.sleep(3) # To let enough time to read the text

display_3s_countdown(window='test window', images=None)

有时数字3不会显示,窗口test window的显示会直接从'some random text'转到数字2。

最后,通常情况下,当我将鼠标放在活动的 cv2 窗口上时,这种行为通常与滚轮鼠标成对出现(无论如何,一次只打开一个窗口)。正如您可能已经从我的文件标题中猜到的那样,我正在使用 macOS。但是,这种行为也会出现在 Windows 上。

我对openCV不是很熟悉,感谢您提供的任何提示和信息。

编辑:我的一些显示是动态的,一旦在 while 循环中创建图像就会显示。因此,显示的效率和速度很重要。

【问题讨论】:

  • 您确定您的图像不共享矩阵元素数据吗?至少在 C++ 中的 OpenCV 试图不为每个矩阵创建新数据,而是重用数据。如果您不深度复制矩阵,则数据是共享的。在您的代码中,如果某些矩阵共享数据,您将多次显示同一张图像,它看起来就像您准备的最后一张图像(在具有共享数据的图像中)。
  • 你能试试images.append(Visual.img.deep-copy)之类的吗?
  • @Micka 我不确定我是否完全理解这一点。我猜你的意思是当向现有图像添加元素时,openCV 不会从头开始重新创建矩阵。是否也适用于图像的显示,即如果 2 个图像共享一个公共部分,例如背景,那这部分就不用重播了?在 3 秒倒计时示例中,数组(img 属性)由 draw_background() 方法初始化,该方法使用 np.full() 创建背景颜色。我正在创建 3 个不同的对象,每秒一个,因此 3 个矩阵(即 Visual.img)是不同的对象。
  • 您能否尝试在 3 个不同的窗口中显示您的 display_3s_countdown 示例代码的所有 3 个图像,仅用于测试?
  • @Micka 按预期工作。以 3 位数字创建 3 个窗口,间隔为 1 秒。奇怪的是倒计时功能在大多数情况下都能正常工作。就像此功能不是跳过显示的唯一情况一样。例如我有一个循环 5 次运行,从一开始显示的指令开始,然后显示一个注视十字,然后是大约 20 秒的声音刺激。出于某种原因,在一个循环期间,固定十字没有显示,并且显示一直停留在该循环的指令上,直到下一次 imshow 调用(下一个循环的指令)。

标签: python numpy opencv


【解决方案1】:

我建议您不要在 GUI 程序中使用time.sleep(OpenCV 的 imshow 算作 GUI)。

使用waitKey() 的延迟参数。通过 1000 获得一秒的最大延迟。

如果 waitKey() 运行不足,GUI 会锁定。 waitKey 运行事件/消息循环。

当您只睡一秒钟时,整个 GUI 就无法进行重要的内务处理。

【讨论】:

    【解决方案2】:

    OpenCV 的图像显示对我来说一直是一个真正的问题。也许我没有从 OpenCV 的基础知识或实际原因开始,但我发现 matplotlib 在这里真的很有帮助。我想给你的最大提示,特别是在图像的情况下: 请改用matplotlib's imshow。

    我使用 matploblib 进行图像显示的最大原因是我不想每次都打开一个新窗口来显示图像(有些人可能更喜欢这样,比如你正在使用脚本)。

    除了 cv2.imshow() 之外,除了你必须编写的多个其他内容之外,更好的是:

    import matplotlib.pyplot as plt
    plt.imshow(img)
    

    不仅如此,您还可以在这里做很多其他事情。

    如删除轴,添加:

    plt.axis('off')
    

    或者有多个图像作为输出,使用subplot。

    注意- 与 OpenCV 相比,使用 matplotlib 输出的图像会有所不同。一个简单的原因是,如果使用 OpenCV 读取图像,图像的颜色空间将是 BGR。而 matplotlib 将图像读取为 RGB。为此,您首先需要在使用 matplotlib 绘图之前将图像转换为 RGB。或者更确切地说,使用 matplotlib 本身导入图像。 Here's a great answer 在这里解释问题。

    【讨论】:

    • 与 openCV 相比,Matplotlib 太慢了。我的一些视觉效果(上面的代码中没有显示)是动态的,即它们在 while 循环中创建后立即显示。 openCV 效率更高,并且允许更高的刷新率,这是我的用例所需要的。
    • 我明白了,在那种情况下,我的回答完全无关紧要。道歉。
    • 根据我的问题你猜不到。我会在编辑中添加它。感谢您的尝试:) 赞成,这个答案可以帮助其他有类似问题但不需要高刷新率的人。我通常也是 matplotlib 用户 ;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-09-18
    • 2021-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-09
    相关资源
    最近更新 更多