【发布时间】: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 调用(下一个循环的指令)。