У меня есть класс для открытия изображения и рисования кругов. Весь код можно найти здесь:
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
Где здесь рекурсия?
@HansHirse хорошая идея, хотя я пробовал использовать self.callback_is_already_set
, чтобы обратный вызов создавался только один раз. Та же проблема
Нашел время... Видимо, дело в 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% моей проблемы решено!
Просто предположение без времени для дальнейшего изучения этого вопроса: возможно, переместите
cv2.setMouseCallback(window_name, self._on_mouse_interact)
(и всю инициализацию окна) в часть__init__
. При каждомredraw
вы повторно создаете обратный вызов - может быть, проблема в этом. Обратный вызов также должен жить в течение времени жизни объекта.