1

I'm writing a code using Python 3.8 where the user is asked to press a button as soon as an image appears. The image is then replaced with a white screen for a random interval of 1 to 4 seconds, until a new image appears. I used cv2.waitKey() until the spacebar gets pressed: no problem, works just fine. See below:

    import sys, cv2, random

    im = cv2.imread('image.tiff')                    
    imBlank = cv2.imread('imageBlank.tiff')            
    cv2.namedWindow('win', cv2.WINDOW_NORMAL) 

    for i in range(5):           # create a loop that runs 5 times
        cv2.imshow('win', im)    # show the image
        k = cv2.waitKey(0)       # wait until key press           
        if k == 32:              # when space bar is pressed
            cv2.imshow('win', imBlank)  # show a blank screen
            SI = random.randint(1,4)    # random interval between 1 and 4 seconds
            cv2.waitKey(SI*1000)        # wait for random interval until loop starts again

Now the problem is that I want to do exactly this, but instead of the space bar, I want the user to press a mouse button. For as far as I can find, the cv2.waitKey() function does not work with the mouse, so I wrote the code below:

    def showBlank(event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN:
            cv2.imshow('win', imBlank)
        elif event == cv2.EVENT_RBUTTONDOWN:
            cv2.imshow('win', imBlank)              
            
    # repeat the loop 5 times
    for i in range(5):
        cv2.imshow('win', im)                       # show the image
        cv2.setMouseCallback('win', showBlank)      # go to function showBlank()
        """ wait here until mouse button is clicked"""
        SI = random.randint(1,4)                    # random interval between 1 and 4 seconds
        cv2.waitKey(SI*1000)                        # wait for random interval until loop starts again

Again, this code basically works, but if the user does not press the mouse button, the loop keeps running anyways. I want the loop to wait for the random interval to start until the mouse is clicked.

Thanks! I hope someone can help me out here

nouk
  • 13
  • 3

1 Answers1

2

I developed this solution which worked on my computer using Python 3.8.0.

import sys, cv2, random

def swapImages():
    cv2.imshow('win', imBlank)
    SI = random.randint(1,4)
    cv2.waitKey(SI*1000)
    cv2.imshow('win', im)    

def showBlank(event, x, y, flags, param):
    #param is the array i from below
    if event == cv2.EVENT_LBUTTONDOWN:
        print("showBlank: i[0]: ", param)
        swapImages()
        param[0] = param[0] + 1
    elif event == cv2.EVENT_RBUTTONDOWN:
        print("showBlank: i[0]: ", param)
        swapImages()
        param[0] = param[0] + 1
                    
im = cv2.imread('image.tiff')                    
imBlank = cv2.imread('imageBlank.tiff')            
cv2.namedWindow('win', cv2.WINDOW_NORMAL) 

i = [0]
# the mousecallback only needs to be set once
cv2.setMouseCallback('win', showBlank, i )
        
# show the initial image for the first time.
cv2.imshow('win', im)

while i[0] < 5:    
  cv2.waitKey(10)

According to this it is not posssible to wait for a mouse click the same way, one can wait for a keyboard input. Because of that, I needed to alter the original code a bit. What the above code does, is to run a while loop until the variable i[0] reaches the value 5. i[0] is increased each time, the image was clicked.

A few explanations to the code:

The array

i = [0]

is passed to the mousecallback via the "param" parameter.

cv2.setMouseCallback('win', showBlank, i )

As described in this, "i" needs to be a container, because

Python passes references to objects. Inside your function you have an object -- You're free to mutate that object (if possible). However, integers are immutable. One workaround is to pass the integer in a container which can be mutated

i[0] changes at each mouse event

param[0] = param[0] + 1

and when i[0] reaches 5, the while loop ends.

Dharman
  • 30,962
  • 25
  • 85
  • 135
captainmoron
  • 270
  • 2
  • 6