如何使用cv2鼠标事件关注类的特定实例



我正在做一个有趣的练习,我画了一个样条,并使用四个手柄点控制它。我希望能够单独拖动四个点中的任何一个,如果我为句柄对象的每个实例硬编码一个特定的状态,这就可以很好地工作,但是我想使拖动事件成为点类的一部分,这样我就可以生成尽可能多的句柄。但是现在我有点不知所措,因为虽然我可以得到最近定义的对象来显示正确的行为,但cv2鼠标回调似乎一次只尊重一个对象。

import cv2
import numpy as np

# Lerp for handles
def lerp(start, end, dt):
lerp_list = []
t = 1
while t >= 0:
x = start[0] + (end[0] - start[0]) * t
y = start[1] + (end[1] - start[1]) * t
lerp_list.append([x, y])
t = round(t-dt, 2)
return np.int32(lerp_list)

# Lerp for spline
def lerp_spline(l1, l2):
lerp_list = []
for pt in range(len(l1)):
t = pt/(len(l1)-1)
x = l1[pt][0] + (l2[pt][0] - l1[pt][0]) * t
y = l1[pt][1] + (l2[pt][1] - l1[pt][1]) * t
lerp_list.append([x, y])
return np.int32(lerp_list)

class Handle:
def __init__(self, x, y):
self.dragging = False
self.x = x + origin[0]
self.y = y + origin[1]
self.pt = [self.x, self.y]
self.radius = 5
def on_mouse_event(self, event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
if (x - self.x)**2 + (y - self.y)**2 < self.radius**2:
self.dragging = True
print("click")
elif event == cv2.EVENT_MOUSEMOVE:
if self.dragging:
self.x = x
self.y = y
self.pt = [self.x, self.y]
print("dragging")
elif event == cv2.EVENT_LBUTTONUP:
self.dragging = False
print("release")

# Canvas Params
height = 512
width = 512
canvas = np.zeros((height, width, 3), dtype=np.uint8)
origin = [int(width/2), int(height/2)]
# Initial Points for Handles
radius = 5
p0 = Handle(0, -150)
p1 = Handle(-224, 200)
p2 = Handle(192, 0)
p3 = Handle(232, 192)
# Mouse events
cv2.namedWindow('Spline')
cv2.setMouseCallback('Spline', p0.on_mouse_event, param=p0)
cv2.setMouseCallback('Spline', p1.on_mouse_event, param=p1)
cv2.setMouseCallback('Spline', p2.on_mouse_event, param=p2)
cv2.setMouseCallback('Spline', p3.on_mouse_event, param=p3)
while True:
# Wipe canvas each frame
canvas = np.zeros((height, width, 3), dtype=np.uint8)
# Spline Functions
t = .05
lp0 = lerp(p0.pt, p1.pt, t)
lp1 = lerp(p3.pt, p2.pt, t)
spline = lerp_spline(lp0, lp1)
# Draw Spline
canvas = cv2.polylines(canvas, [spline], False, (255, 255, 0), 1, lineType=cv2.LINE_AA)
# Draw Handles
canvas = cv2.circle(canvas, lp0[0], 2, (255, 0, 0), -1, lineType=cv2.LINE_AA)
canvas = cv2.circle(canvas, lp1[0], 2, (255, 255, 0), -1, lineType=cv2.LINE_AA)
canvas = cv2.circle(canvas, lp0[-1], 2, (0, 255, 255), -1, lineType=cv2.LINE_AA)
canvas = cv2.circle(canvas, lp1[-1], 2, (0, 0, 255), -1, lineType=cv2.LINE_AA)
canvas = cv2.line(canvas, p0.pt, p1.pt, (255, 255, 255), 1, lineType=cv2.LINE_AA)
canvas = cv2.line(canvas, p2.pt, p3.pt, (255, 255, 255), 1, lineType=cv2.LINE_AA)
# Show Canvas
cv2.imshow('Spline', canvas)
if cv2.waitKey(16) == ord('q'):
break
cv2.destroyAllWindows()

为大量代码道歉。它完全可以运作。我关心的部分是从Handle类开始的

目前,只有最近引用的Handle对象p3显示了正确的功能。为了简化示例,我将鼠标回调重复了四次,每个Handle对象重复一次。这不是一个优雅的解决方案,但希望能说明问题。

意图和结果:我希望Handle类的行为类似于CSS类,其中鼠标事件自动分配给类的所有实例,并且每个实例单独执行。这当然是一厢情愿。最终的结果是,似乎只有最后的鼠标回调才算。所以当脚本检查鼠标是否靠近self时。X或self。Y,它只检查最近定义的坐标

谢谢你的帮助!

我想到了一个解决办法。通过调用on_mouse_event()作为递归函数,它可以工作。

def on_mouse_event(event, x, y, flags, param):
for handle in handles:
handle.on_mouse_event(event, x, y, flags, param)

然后在一个鼠标回调中调用这个函数:

cv2.setMouseCallback('Spline', on_mouse_event)

希望有一天这个不起眼的小问题能帮到别人。

最新更新