I don't think it's possible with that approach - but am always happy to be corrected and learn something new. I know of a couple of workarounds.
The first is to continue to use pyautogui
and to call its mouseposition()
function and paste/draw your own synthetic mouse pointer onto the grab. I did that with OpenCV's fillPoly()
function:
#!/usr/bin/env python3
import cv2
import numpy as np
import pyautogui
import datetime
# X and Y coordinates of mouse pointer
Xs = [0,8,6,14,12,4,2,0]
Ys = [0,2,4,12,14,6,8,0]
while True:
img = pyautogui.screenshot()
mouseX,mouseY = pyautogui.position()
mouseX *= 2
mouseY *= 2
frame = np.array(img)
frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
# Synthesize mouse pointer
Xthis = [4*x+mouseX for x in Xs]
Ythis = [4*y+mouseY for y in Ys]
points = list(zip(Xthis,Ythis))
points = np.array(points, 'int32')
cv2.fillPoly(frame,[points],color=[255,255,255])
# Make it a bit smaller for display
frame = cv2.resize(frame,(960,540))
cv2.imshow('Title', frame)
if cv2.waitKey(1) == ord("q"):
break
cv2.destroyAllWindows()
out.release()
The second is to use ffmpeg
which can capture the mouse - you can either run ffmpeg
in place of your current app, or pipe the output from ffmpeg
into your app through a pipe and continue to process it as you are now. That might look like this:
#!/usr/bin/env python3
# ffmpeg -y -pix_fmt bgr0 -f avfoundation -r 20 -t 10 -i 1 -vf scale=w=3840:h=2160 -f rawvideo /dev/null
import sys
import cv2
import time
import subprocess
import numpy as np
w,h = 3840, 2160
def ffmpegGrab():
"""Generator to read frames from ffmpeg subprocess"""
cmd = [
'ffmpeg',
'-pix_fmt', 'bgr0',
'-f', 'avfoundation',
'-capture_cursor', '1',
'-capture_mouse_clicks', '1',
'-r', '20',
'-i', '1',
'-vf','scale=w=3840:h=2160',
'-f', 'rawvideo',
'pipe:1'
]
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while True:
frame = proc.stdout.read(w*h*4)
yield np.frombuffer(frame, dtype=np.uint8).reshape((h,w,4))
# Get frame generator
gen = ffmpegGrab()
# Get start time
start = time.time()
# Read video frames from ffmpeg in loop
nFrames = 0
while True:
# Read next frame from ffmpeg
frame = next(gen)
nFrames += 1
frame = cv2.resize(frame,(960,540))
cv2.imshow('screenshot', frame)
if cv2.waitKey(1) == ord("q"):
break
fps = nFrames/(time.time()-start)
print(f'FPS: {fps}')
cv2.destroyAllWindows()
out.release()
Note that pyautogui takes around 600ms to capture one frame on my Mac, whereas the ffmpeg
above achieves around 20fps, or 50ms per frame.
Keywords: Python. image processing, ffmpeg, pyautogui, screen-grab, screen-capture, screengrab, screencapture, fps. speed, prime.