1

I want to have the label_image() code into a function that is initialized with an image, i.e., label_image(image). Right now, if I do not declare the image as global variable, it does not properly update because of the callback function having image as global vs label_image(image) having a local copy of image. Also, despite the callback function click() being initialized inside label_image(), it cannot see the variable image unless it is declared globally. I guess my problem would be easily solved if I could pass image to click() which then would be able to update the same copy that label_image(image) received. So far, I didn't find anyway to do so as the callback function expects the 5 parameters event, x, y, flags, param...

def click(event, x, y, flags, param):
        global click_pts, image_storage, image
        if event == cv2.EVENT_LBUTTONDOWN:
            click_pts.append((x, y, 1))
            cv2.circle(image, (x, y), 5, (255, 0, 0), -1)
            image_storage = lshift(image_storage, image)
            cv2.imshow("image", image_storage[-1])
            print('added %(n)s, size %(s)s, type %(t)s' % {'n': (x, y), 's': len(click_pts), 't': 1})
        elif event == cv2.EVENT_RBUTTONDOWN:
            click_pts.append((x, y, 2))
            cv2.circle(image, (x, y), 5, (0, 255, 255), -1)
            image_storage = lshift(image_storage, image)
            cv2.imshow("image", image_storage[-1])
            print('added %(n)s, size %(s)s, type %(t)s' % {'n': (x, y), 's': len(click_pts), 't': 2})


def label_image() -> list:
    global click_pts, image_storage, image
    click_pts = []
    image_storage = np.zeros((10,) + image.shape, np.uint8)
    image_storage[:] = np.ndarray.copy(image)
    cv2.namedWindow('image', cv2.WINDOW_NORMAL)
    cv2.resizeWindow('image', 1400, 1400)
    cv2.setMouseCallback("image", click)

    # Loop until 'q' is pressed
    while True:
        cv2.imshow("image", image_storage[-1])
        key = cv2.waitKey(1) & 0xFF
        if key == ord('q'):
            break
        elif key == ord('b'):
            try:
                print('removed: %(n)s, size %(s)s' % {'n': click_pts[-1], 's': len(click_pts)})
                click_pts.pop()
            except IndexError:
                print('the array is empty')
            image_storage = rshift(image_storage)
            image = np.ndarray.copy(image_storage[-1])
            cv2.imshow("image", image_storage[-1])

    cv2.destroyAllWindows()
    return click_pts

image = cv2.imread('someimage.png')
label_image()
10
  • 1
    in the setMouseCallback use the optional "param" variable Commented Jun 12, 2020 at 14:00
  • 1
    try this cv2.setMouseCallback("image", click, image) then i think the image get passed to param .Have a look at this also answers.opencv.org/question/32888/… Commented Jun 12, 2020 at 14:37
  • 1
    in C++ you would pass the address of image to setMouseCallback (maybe casted to void*) and in your actual callback function you would cast it to the image's class again. In your running code you would change image's content, but not its address. Commented Jun 12, 2020 at 14:51
  • 1
    have a look at the comments and answers here: stackoverflow.com/questions/23596511/… Commented Jun 12, 2020 at 19:20
  • 1
    @Micka, thank you so much! Ahh I had this intuition but I was confused having the callback nested inside the class... quite straightforward! Commented Jun 12, 2020 at 20:01

1 Answer 1

0

Given that you want to end up calling this:

def click(image, event, x, y, flags, param):

so that we are passing in image as the first parameter, we can make a partial function which has the image parameter on the front already, leaving the callback code to apply the normal event, x, y, flags, param params just as normal.

This means making a partial for the callback:

def label_image():
    # all the usual stuff elided ...

    partial_click = partial(click, image)  # here image is applied first
    cv2.setMouseCallback("image", partial_click)
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.