Избегайте ошибки рекурсии с помощью cv2 и обратного вызова мыши

У меня есть класс для открытия изображения и рисования кругов. Весь код можно найти здесь:

p1 и p2 хранят диаметрально противоположные точки на окружности. Это захват с помощью действий щелчка и перетаскивания в _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)

Его можно запустить с

python script.py path/to/image/file

Моя проблема в том, что я получаю

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

Где здесь рекурсия?

Просто предположение без времени для дальнейшего изучения этого вопроса: возможно, переместите cv2.setMouseCallback(window_name, self._on_mouse_interact) (и всю инициализацию окна) в часть __init__. При каждом redraw вы повторно создаете обратный вызов - может быть, проблема в этом. Обратный вызов также должен жить в течение времени жизни объекта.

HansHirse 15.12.2020 14:39

@HansHirse хорошая идея, хотя я пробовал использовать self.callback_is_already_set, чтобы обратный вызов создавался только один раз. Та же проблема

Alexander Soare 15.12.2020 14:42
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
0
2
119
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Нашел время... Видимо, дело в waitKey. Я переместил несколько строк из redraw в __call__, и теперь я больше не получаю ошибку:

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)

Не могли бы вы проверить, работает ли это для вас?

Это сработало хорошо. До сих пор не понимаю почему, но думаю, что это 80% моей проблемы решено!

Alexander Soare 15.12.2020 15:05

Другие вопросы по теме