I am displaying some very simple numpy array with openCV by using cv2.imshow()
and cv2.waitKey()
. Sometimes, I want a display to stay for a few seconds and then resume my program. I simply add a time.sleep()
with the desired sleep value. Most of the time, everything works fine; however, sometimes the display doesn't change and remains on the previous one. It is very inconsistent. The same code might work 80% of the time, and might not work for the remaining 20%. Interestingly enough, it always seems to be the same image/display which doesn't work across multiple runs; although it does work as well sometimes. As I said, it's very inconsistent.
Here is a small portion of the code I used to create and show the displays:
#!/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)
Sadly, I am unable to reproduce the problem with small piece of code. But I can demonstrate with the following function:
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)
As you can see, this small function simply creates 3 images, each with a different number written, in order to create a small 3 seconds countdown. When I do something like:
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)
Sometimes, the number 3 will not be displayed, and the display in the window test window
will directly go from 'some random text'
to the number 2.
Finally, usually, this behavior goes in pairs with a spinning wheel mouse when I have the mouse on the active cv2 window (only one window is opened at a time anyway). As you might have guessed from the header of my file, I am working on macOS. However, this behavior also appears on Windows.
I am not very familiar with openCV, thank you for any tip and information you could provide.
EDIT: Some of my displays are dynamic and are shown as soon as the image is created within a while loop. Thus, efficiency and speed in the display are important.