3

I am making a screen recorder that records the screen of the desktop. However, when I record the screen, the mouse pointer (cursor) is not visible in the recording. Is there any way I can show the mouse pointer in my recording? This is my current code:

import cv2
import numpy as np
import pyautogui
import datetime

date = datetime.datetime.now()

SCREEN_SIZE = (1366, 768)
framerate = 12

fourcc = cv2.VideoWriter_fourcc(*'XVID')
filename = 'E:/project/videos/rec_%s%s%s%s%s%s.avi' % (date.year, date.month, date.day, date.hour, date.minute, date.second)

out = cv2.VideoWriter(filename, fourcc,framerate, SCREEN_SIZE)

while True:
    img = pyautogui.screenshot()
    frame = np.array(img)
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    out.write(frame)
    cv2.imshow('screenshot', frame)
    if cv2.waitKey(1) == ord("q"):
        break

cv2.destroyAllWindows()
out.release()
wovano
  • 4,543
  • 5
  • 22
  • 49
Cb9867
  • 91
  • 1
  • 2
  • 10

1 Answers1

3

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()

enter image description here


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()

enter image description here

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.

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • 1
    Note that the `-f` and `-i` parameters to `ffmpeg` will differ under Windows. – Mark Setchell Feb 17 '20 at 20:08
  • i am using a windows os and the first method that you showed with pyautogui is not working it is showing the cursor only in the frame which is created by opencv and not in the showing in the recording when i minimize the frame window. – Cb9867 Feb 18 '20 at 05:22
  • 1
    Sorry, I don't use Windows. I can only suggest to print the mouse position on the Terminal just after getting it and to print the `points` array just before the `cv2.fillPoly()` call. – Mark Setchell Feb 18 '20 at 07:27