Skip to content
Advertisement

Avoid recursion error with cv2 and mouse callback

I have a class for opening up an image and drawing circles. The entire code can be found here:

p1 and p2 store diametrically opposing points on a circle. These are capture with click and drag actions in _on_mouse_interact.

import sys
import math

import cv2


class DrawCircle:
    def __init__(self):
        self.p1 = None
        self.p2 = None
        self.clicked = False

    def __call__(self, img):
        self.img = img
        self.redraw()
        return self.p1, self.p2

    def redraw(self):
        drawn = self.img.copy()
        if self.p1 is not None and self.p2 is not None:
            center = (int(round((self.p1[0] + self.p2[0]) / 2)),
                        int(round((self.p1[1] + self.p2[1]) / 2)))
            radius = int(math.sqrt(
                (self.p1[0] - self.p2[0])**2 + (self.p1[1] - self.p2[1])**2) / 2)
            cv2.circle(drawn, center, radius, (0, 0, 255), thickness=1)
        window_name = 'draw circle'
        cv2.imshow(window_name, drawn)
        cv2.setMouseCallback(window_name, self._on_mouse_interact)
        k = cv2.waitKey(0)
        if k == ord('q'):
            cv2.destroyAllWindows()
            return

    def _on_mouse_interact(self, event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN:
            self.clicked = True
            self.p1 = [x, y]
            self.p2 = None
        if event == cv2.EVENT_MOUSEMOVE:
            if self.clicked:
                self.p2 = [x, y]
                self.redraw()
        if event == cv2.EVENT_LBUTTONUP:
            self.clicked = False
            self.p2 = [x, y]
            self.redraw()


if __name__ == '__main__':
    draw_tool = DrawCircle()
    img = cv2.imread(sys.argv[1])
    draw_tool(img)

It can be run with

python script.py path/to/image/file

My issue is that I get

Traceback (most recent call last):
  File "utils/realtime_draw.py", line 47, in _on_mouse_interact
    self.redraw()
  File "utils/realtime_draw.py", line 24, in redraw
    drawn = self.img.copy()
RecursionError: maximum recursion depth exceeded while calling a Python object

Where is the recursion here?

Advertisement

Answer

Found some time… Apparently, it’s the waitKey stuff. I moved some lines from the redraw to the __call__, and now, I don’t get the error anymore:

import sys
import math

import cv2


class DrawCircle:
    def __init__(self):
        self.p1 = None
        self.p2 = None
        self.clicked = False

    def __call__(self, img):
        self.img = img
        self.window_name = 'draw circle'
        cv2.imshow(self.window_name, self.img)
        cv2.setMouseCallback(self.window_name, self._on_mouse_interact)
        self.redraw()
        k = cv2.waitKey(0)
        if k == ord('q'):
            cv2.destroyAllWindows()
            return
        return self.p1, self.p2

    def redraw(self):
        drawn = self.img.copy()
        if self.p1 is not None and self.p2 is not None:
            center = (int(round((self.p1[0] + self.p2[0]) / 2)),
                        int(round((self.p1[1] + self.p2[1]) / 2)))
            radius = int(math.sqrt(
                (self.p1[0] - self.p2[0])**2 + (self.p1[1] - self.p2[1])**2) / 2)
            cv2.circle(drawn, center, radius, (0, 0, 255), thickness=1)
        cv2.imshow(self.window_name, drawn)

    def _on_mouse_interact(self, event, x, y, flags, param):
        if event == cv2.EVENT_LBUTTONDOWN:
            self.clicked = True
            self.p1 = [x, y]
            self.p2 = None
        if event == cv2.EVENT_MOUSEMOVE:
            if self.clicked:
                self.p2 = [x, y]
                self.redraw()
        if event == cv2.EVENT_LBUTTONUP:
            self.clicked = False
            self.p2 = [x, y]
            self.redraw()


if __name__ == '__main__':
    draw_tool = DrawCircle()
    img = cv2.imread(sys.argv[1])
    draw_tool(img)

Could you please check, whether that works for you?

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement