我试图在 Pyautogui 中以贝塞尔曲线运动移动鼠标,以模拟更多的人体运动,如下所示:
pyautogui 中有一些补间/缓动功能,但没有一个代表贝塞尔曲线类型的移动。我创建了一个小脚本来计算它在最终到达目的地之前会到达的随机位置。
不幸的是,每个目的地鼠标暂时停止。
import pyautogui
import time
import random
print "Randomized Mouse Started."
destx = 444;
desty = 631;
x, y = pyautogui.position() # Current Position
moves = random.randint(2,4)
pixelsx = destx-x
pixelsy = desty-y
if moves >= 4:
moves = random.randint(2,4)
avgpixelsx = pixelsx/moves
avgpixelsy = pixelsy/moves
print "Pixels to be moved X: ", pixelsx," Y: ",pixelsy, "Number of mouse movements: ", moves, "Avg Move X: ", avgpixelsx, " Y: ", avgpixelsy
while moves > 0:
offsetx = (avgpixelsx+random.randint(-8, random.randint(5,10)));
offsety = (avgpixelsy+random.randint(-8, random.randint(5,10)));
print x + offsetx, y + offsety, moves
pyautogui.moveTo(x + offsetx, y + offsety, duration=0.2)
moves = moves-1
avgpixelsx = pixelsx / moves
avgpixelsy = pixelsy / moves
信息:
我看过这篇文章:python random mouse movements
但无法弄清楚如何定义“开始和停止”位置。答案非常接近我正在寻找的东西。
关于如何实现这个的任何想法?
使用
scipy, numpy
和任何可以简单地移动鼠标光标的东西:
import pyautogui
import random
import numpy as np
import time
from scipy import interpolate
import math
def point_dist(x1,y1,x2,y2):
return math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
cp = random.randint(3, 5) # Number of control points. Must be at least 2.
x1, y1 = pyautogui.position() # Starting position
# Distribute control points between start and destination evenly.
x = np.linspace(x1, x2, num=cp, dtype='int')
y = np.linspace(y1, y2, num=cp, dtype='int')
# Randomise inner points a bit (+-RND at most).
RND = 10
xr = [random.randint(-RND, RND) for k in range(cp)]
yr = [random.randint(-RND, RND) for k in range(cp)]
xr[0] = yr[0] = xr[-1] = yr[-1] = 0
x += xr
y += yr
# Approximate using Bezier spline.
degree = 3 if cp > 3 else cp - 1 # Degree of b-spline. 3 is recommended.
# Must be less than number of control points.
tck, u = interpolate.splprep([x, y], k=degree)
# Move upto a certain number of points
u = np.linspace(0, 1, num=2+int(point_dist(x1,y1,x2,y2)/50.0))
points = interpolate.splev(u, tck)
# Move mouse.
duration = 0.1
timeout = duration / len(points[0])
point_list=zip(*(i.astype(int) for i in points))
for point in point_list:
pyautogui.moveTo(*point)
time.sleep(timeout)
您可以通过设置删除
pyautogui
中的任何内置延迟:
# Any duration less than this is rounded to 0.0 to instantly move the mouse.
pyautogui.MINIMUM_DURATION = 0 # Default: 0.1
# Minimal number of seconds to sleep between mouse moves.
pyautogui.MINIMUM_SLEEP = 0 # Default: 0.05
# The number of seconds to pause after EVERY public function call.
pyautogui.PAUSE = 0 # Default: 0.1
P.S.:上面的示例不需要任何这些设置,因为它不使用公共
moveTo
方法。
numpy
与 bezier
库一起使用:import pyautogui
import bezier
import numpy as np
# Disable pyautogui pauses (from DJV's answer)
pyautogui.MINIMUM_DURATION = 0
pyautogui.MINIMUM_SLEEP = 0
pyautogui.PAUSE = 0
# We'll wait 5 seconds to prepare the starting position
start_delay = 5
print("Drawing curve from mouse in {} seconds.".format(start_delay))
pyautogui.sleep(start_delay)
# For this example we'll use four control points, including start and end coordinates
start = pyautogui.position()
end = start[0]+600, start[1]+200
# Two intermediate control points that may be adjusted to modify the curve.
control1 = start[0]+125, start[1]+100
control2 = start[0]+375, start[1]+50
# Format points to use with bezier
control_points = np.array([start, control1, control2, end])
points = np.array([control_points[:,0], control_points[:,1]]) # Split x and y coordinates
# You can set the degree of the curve here, should be less than # of control points
degree = 3
# Create the bezier curve
curve = bezier.Curve(points, degree)
# You can also create it with using Curve.from_nodes(), which sets degree to len(control_points)-1
# curve = bezier.Curve.from_nodes(points)
curve_steps = 50 # How many points the curve should be split into. Each is a separate pyautogui.moveTo() execution
delay = 1/curve_steps # Time between movements. 1/curve_steps = 1 second for entire curve
# Move the mouse
for i in range(1, curve_steps+1):
# The evaluate method takes a float from [0.0, 1.0] and returns the coordinates at that point in the curve
# Another way of thinking about it is that i/steps gets the coordinates at (100*i/steps) percent into the curve
x, y = curve.evaluate(i/curve_steps)
pyautogui.moveTo(x, y) # Move to point in curve
pyautogui.sleep(delay) # Wait delay
我想出了这个,试图写一些东西来用鼠标绘制SVG路径。运行上面的代码将使您的鼠标沿着与下面相同的路径移动。红点位于定义曲线的每个控制点上。
请注意,如果您想像我在 GIMP 中所做的那样单击并拖动,则必须在脚本末尾的循环之前和之后添加
pyautogui.mouseDown()
和 pyautogui.mouseUp()
:
您可以在此处查看
bezier
文档:https://bezier.readthedocs.io/en/stable/index.html
你只需要知道是
move_mouse((300,300))
会让你的鼠标到达(300,300),然后永远不会改变。看看实现,它只是调用WIN32 apimouse_event
。阅读一些关于它的东西,你会发现没有“开始和停止”位置。我不知道如何绘制贝塞尔曲线。
while True:
pos = (random.randrange(*x_bound),random.randrange(*y_bound))
move_mouse(pos)
time.sleep(1.0/steps_per_second)
看,这就是动画的秘密。你需要做的就是写一个
pos = draw_bezier_curve(t)
一点更新(2023 年)——我已经使用它一段时间了。这个方法使用了
numpy
、pytweening
和pyautogui
的组合:
import numpy as np
import pyautogui as pag
import pytweening
from pyclick import HumanCurve
class MouseUtils:
def move_to(destination: tuple, **kwargs):
# sourcery skip: use-contextlib-suppress
"""
Use Bezier curve to simulate human-like mouse movements.
Args:
destination: x, y tuple of the destination point
destination_variance: pixel variance to add to the destination point (default 0)
Kwargs:
knotsCount: number of knots to use in the curve, higher value = more erratic movements
(default determined by distance)
mouseSpeed: speed of the mouse (options: 'slowest', 'slow', 'medium', 'fast', 'fastest')
(default 'fast')
tween: tweening function to use (default easeOutQuad)
"""
offsetBoundaryX = kwargs.get("offsetBoundaryX", 100)
offsetBoundaryY = kwargs.get("offsetBoundaryY", 100)
knotsCount = kwargs.get("knotsCount", MouseUtils.__calculate_knots(destination))
distortionMean = kwargs.get("distortionMean", 1)
distortionStdev = kwargs.get("distortionStdev", 1)
distortionFrequency = kwargs.get("distortionFrequency", 0.5)
tween = kwargs.get("tweening", pytweening.easeOutQuad)
mouseSpeed = kwargs.get("mouseSpeed", "fast")
mouseSpeed = MouseUtils.__get_mouse_speed(mouseSpeed)
dest_x = destination[0]
dest_y = destination[1]
start_x, start_y = pag.position()
for curve_x, curve_y in HumanCurve(
(start_x, start_y),
(dest_x, dest_y),
offsetBoundaryX=offsetBoundaryX,
offsetBoundaryY=offsetBoundaryY,
knotsCount=knotsCount,
distortionMean=distortionMean,
distortionStdev=distortionStdev,
distortionFrequency=distortionFrequency,
tween=tween,
targetPoints=mouseSpeed,
).points:
pag.moveTo((curve_x, curve_y))
start_x, start_y = curve_x, curve_y
def move_rel(self, x: int, y: int, x_var: int = 0, y_var: int = 0, **kwargs):
"""
Use Bezier curve to simulate human-like relative mouse movements.
Args:
x: x distance to move
y: y distance to move
x_var: random upper-bound pixel variance to add to the x distance (default 0)
y_var: random upper-bound pixel variance to add to the y distance (default 0)
Kwargs:
knotsCount: if right-click menus are being cancelled due to erratic mouse movements,
try setting this value to 0.
"""
if x_var != 0:
x += np.random.randint(-x_var, x_var)
if y_var != 0:
y += np.random.randint(-y_var, y_var)
self.move_to((pag.position()[0] + x, pag.position()[1] + y), **kwargs)
def __calculate_knots(destination: tuple):
"""
Calculate the knots to use in the Bezier curve based on distance.
Args:
destination: x, y tuple of the destination point
"""
# calculate the distance between the start and end points
distance = np.sqrt((destination[0] - pag.position()[0]) ** 2 + (destination[1] - pag.position()[1]) ** 2)
res = round(distance / 200)
return min(res, 3)
def __get_mouse_speed(speed: str) -> int:
"""
Converts a text speed to a numeric speed for HumanCurve (targetPoints).
"""
if speed == "slowest":
return rd.randint(85, 100)
elif speed == "slow":
return rd.randint(65, 80)
elif speed == "medium":
return rd.randint(45, 60)
elif speed == "fast":
return rd.randint(20, 40)
elif speed == "fastest":
return rd.randint(10, 15)
else:
raise ValueError("Invalid mouse speed. Try 'slowest', 'slow', 'medium', 'fast', or 'fastest'.")