【问题标题】:Capture video data from screen in Python在 Python 中从屏幕上捕获视频数据
【发布时间】:2016-05-07 23:22:57
【问题描述】:

有没有办法使用 Python(可能使用 OpenCV 或 PIL)连续抓取屏幕的全部或部分帧,至少 15 fps 或更高?我已经看到它用其他语言完成,所以理论上应该是可能的。

我不需要将图像数据保存到文件中。我实际上只是希望它输出一个包含原始 RGB 数据的数组(比如在一个 numpy 数组或其他东西中),因为我将把它发送到一个大型 LED 显示器(可能在重新调整大小之后)。

【问题讨论】:

标签: python opencv numpy screenshot


【解决方案1】:

您将需要使用 Pillow (PIL) 库中的 ImageGrab 并将捕获转换为 numpy 数组。当您拥有数组时,您可以使用 opencv 随心所欲地使用它。我将捕获转换为灰色并使用 imshow() 作为演示。

这是一个快速入门的代码:

from PIL import ImageGrab
import numpy as np
import cv2

img = ImageGrab.grab(bbox=(100,10,400,780)) #bbox specifies specific region (bbox= x,y,width,height *starts top-left)
img_np = np.array(img) #this is the array obtained from conversion
frame = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
cv2.imshow("test", frame)
cv2.waitKey(0)
cv2.destroyAllWindows()

您可以按照您喜欢的频率插入一个阵列以继续捕获帧。之后,您只需解码帧。不要忘记在循环之前添加:

fourcc = cv2.VideoWriter_fourcc(*'XVID')
vid = cv2.VideoWriter('output.avi', fourcc, 6, (640,480))

您可以在循环内添加:

vid.write(frame) #the edited frame or the original img_np as you please

更新
最终结果看起来像这样(如果你想实现帧流。存储为视频只是在捕获的屏幕上使用 opencv 的演示):

from PIL import ImageGrab
import numpy as np
import cv2
while(True):
    img = ImageGrab.grab(bbox=(100,10,400,780)) #bbox specifies specific region (bbox= x,y,width,height)
    img_np = np.array(img)
    frame = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
    cv2.imshow("test", frame)
    cv2.waitKey(0)
cv2.destroyAllWindows()

希望有帮助

【讨论】:

  • 不知道你的意思是什么:“你可以用你喜欢的频率插入一个阵列以继续捕获帧”...阵列在哪里?而且我根本不需要做视频输出......有没有办法指定屏幕上的抓取位置?
  • 我的意思是说添加一段时间以获取屏幕截图流。我现在将编辑代码。至于指定特定区域使用 bbox 参数。一分钟我会更新
  • 我做了进一步的修改。希望有帮助
  • 这基本上使用ImageGrab.grab(),根据我的经验,它非常慢(在我的 Macbook Pro 2015 上大约 2 fps)。正如 OP 所要求的那样,此解决方案无法以超过 15fps 的速度捕获。我还尝试了 mss 库,它要快得多,但我无法保证稳定的 fps 或图像之间的固定间隔。
  • ImageGrab 仅适用于 macOS 和 Windows
【解决方案2】:

mss 还有一个解决方案可以提供更好的帧速率。 (在装有 MacOS Sierra 的 Macbook Pro 上测试)

import numpy as np
import cv2
from mss import mss
from PIL import Image

mon = {'left': 160, 'top': 160, 'width': 200, 'height': 200}

with mss() as sct:
    while True:
        screenShot = sct.grab(mon)
        img = Image.frombytes(
            'RGB', 
            (screenShot.width, screenShot.height), 
            screenShot.rgb, 
        )
        cv2.imshow('test', np.array(img))
        if cv2.waitKey(33) & 0xFF in (
            ord('q'), 
            27, 
        ):
            break

【讨论】:

  • 我在使用 Image.frombytes 时得到 ValueError: not enough image data
  • 使用 i7 2600k,我在录制 1440p 显示器时只能获得 8fps。
  • 截至 2019 年 7 月,此代码将触发此错误:“AttributeError: 'MSS' object has no attribute 'get_pixels'”。
  • @YakovK 要避免这种情况,请安装旧版本的 mss:pip uninstall msspip install mss==2.0.22
  • 另一种解决方法是使用mss().grab(window) 代码看起来类似于:screenshot = mss.mss().grab(window) img = Image.frombytes("RGB", (screenshot.width, screenshot.height), screenshot.rgb)
【解决方案3】:

你可以试试这个=>

import mss
import numpy

with mss.mss() as sct:
    monitor = {'top': 40, 'left': 0, 'width': 800, 'height': 640}
    img = numpy.array(sct.grab(monitor))
    print(img)

【讨论】:

    【解决方案4】:

    我尝试了以上所有方法,但它没有给我实时屏幕更新。 你可以试试这个。此代码经过测试并成功运行,还为您提供了良好的 fps 输出。您也可以根据需要的每个循环时间来判断。

    import numpy as np
    import cv2
    from PIL import ImageGrab as ig
    import time
    
    last_time = time.time()
    while(True):
        screen = ig.grab(bbox=(50,50,800,640))
        print('Loop took {} seconds',format(time.time()-last_time))
        cv2.imshow("test", np.array(screen))
        last_time = time.time()
        if cv2.waitKey(25) & 0xFF == ord('q'):
            cv2.destroyAllWindows()
            break
    

    【讨论】:

    • 在 Windows 上使用 Python 3.6 进行了尝试,我的 i7 6700k CPU 获得了大约 10 fps。
    • 循环耗时 {} 秒。那是我的 FPS 吗?
    • 不,它是两帧进程之间的时间差…… 差越小越好。您可以进行另一个计算,例如分析每 60 秒处理多少帧的差异...
    • MacMini 上 2.5 fps - 而 Markoe7 的方法渲染速度 > 65 fps
    【解决方案5】:

    我已经从PIL 尝试了ImageGrab,它给了我 20fps,这还可以,但使用 win32 库给了我 +40fps,这太棒了!

    我使用了 Frannecklp 的 this 代码,但它不能正常工作,所以我需要对其进行修改:

    -首先pip install pywin32 以防使用库

    - 像这样导入库:

    import cv2
    import numpy as np
    from win32 import win32gui
    from pythonwin import win32ui
    from win32.lib import win32con
    from win32 import win32api
    

    为了得到一个简单的图像屏幕:

    from grab_screen import grab_screen
    import cv2
    img = grab_screen()
    cv2.imshow('frame',img)
    

    以及获取帧:

    while(True):
    #frame = grab_screen((0,0,100,100))
    frame = grab_screen()
    cv2.imshow('frame',frame)
    if cv2.waitKey(1) & 0xFF == ord('q') or x>150:
        break
    

    【讨论】:

      【解决方案6】:

      根据这篇文章和其他帖子,我做了这样的事情。 它在不保存img的情况下截取屏幕截图并写入视频文件。

      import cv2
      import numpy as np
      import os
      import pyautogui
      
      output = "video.avi"
      img = pyautogui.screenshot()
      img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
      #get info from img
      height, width, channels = img.shape
      # Define the codec and create VideoWriter object
      fourcc = cv2.VideoWriter_fourcc(*'mp4v')
      out = cv2.VideoWriter(output, fourcc, 20.0, (width, height))
      
      while(True):
       try:
        img = pyautogui.screenshot()
        image = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
        out.write(image)
        StopIteration(0.5)
       except KeyboardInterrupt:
        break
      
      out.release()
      cv2.destroyAllWindows()
      

      【讨论】:

      • 谢谢。只有第 18 行的两个错别字,您应该传递 img 而不是 image,而在第 19 行,您应该传递 image 而不是 img。
      • 谢谢,没注意到只是想把它放在一起:D(很快就会修复)
      【解决方案7】:

      通过上述所有解决方案,我无法获得可用的帧速率,直到我通过以下方式修改了我的代码:

      import numpy as np
      import cv2
      from mss import mss
      from PIL import Image
      
      bounding_box = {'top': 100, 'left': 0, 'width': 400, 'height': 300}
      
      sct = mss()
      
      while True:
          sct_img = sct.grab(bounding_box)
          cv2.imshow('screen', np.array(sct_img))
      
          if (cv2.waitKey(1) & 0xFF) == ord('q'):
              cv2.destroyAllWindows()
              break
      

      使用这个解决方案,我可以轻松获得 20+ 帧/秒。

      作为参考,请查看此链接:OpenCV/Numpy example with mss

      【讨论】:

      • Mac Mini 上 65fps - 太棒了!
      • 保存为 .npy 文件是一件轻而易举的事,我每 10-15 毫秒得到一个帧。结果是 100FPS - 67FPS。我猜SSD在这里也扮演着重要的角色。
      • 太棒了,这应该是最佳答案。
      【解决方案8】:

      您可以尝试此代码,因为它对我有用。我已经在 Linux 上测试过了

      import numpy as np
      import cv2
      from mss import mss
      from PIL import Image
      
      sct = mss()
      
      while 1:
          w, h = 800, 640
          monitor = {'top': 0, 'left': 0, 'width': w, 'height': h}
          img = Image.frombytes('RGB', (w,h), sct.grab(monitor).rgb)
          cv2.imshow('test', cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR))
          if cv2.waitKey(25) & 0xFF == ord('q'):
              cv2.destroyAllWindows()
              break
      

      确保已安装以下软件包:

      枕头、opencv-python、numpy、mss

      【讨论】:

      • 很棒的方法,使用这种方法设法获得平均 90 fps
      【解决方案9】:

      这个任务用opencv很简单,我们只是循环截屏,然后转换成帧。我为屏幕录制创建了计时器,在开始时您必须输入要录制的秒数:) 这是代码。

      import cv2
      import numpy as np
      import pyautogui
      from win32api import GetSystemMetrics
      import time
      
      #Take resolution from system automatically
      w = GetSystemMetrics(0)
      h =  GetSystemMetrics(1)
      SCREEN_SIZE = (w,h)
      fourcc = cv2.VideoWriter_fourcc(*"XVID")
      out = cv2.VideoWriter("recording.mp4", fourcc, 20.0, (SCREEN_SIZE))
      tim = time.time()
      tp = int(input('How many times you want to record screen?->(Define value in Seconds): '))
      tp = tp+tp
      f = tim+tp
      while True:
          img = pyautogui.screenshot()
          frame = np.array(img)
          frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
          out.write(frame)
          tu = time.time()
          if tu>f:
              break
      cv2.destroyAllWindows()
      out.release()
      

      这就是您在屏幕录制中使用时间的方式,您不需要使用 imshow(),因为它会在屏幕上无限显示我们的屏幕录制,因此输出视频看起来很奇怪。

      【讨论】:

      • 这可行,但它真的不快,至少在 linux 上它依赖于外部工具 scrot 来实际截取屏幕截图。即使用pyautogui.screenshot(region=(...)) 告诉它只截屏一个小区域,我的 3 台显示器也需要将近半秒的时间。
      【解决方案10】:

      如果有人正在寻找一种更简单、更快捷的方式在 python 中将屏幕作为帧抓取,那么请在任何机器上使用我的高性能视频处理vidgear 库中的ScreenGear API,只需几行 python 代码即可(在所有平台上测试,包括 Windows 10、MacOS Serra、Linux Mint) 并享受线程屏幕投射:

      注意:它还支持开箱即用的多个后端和屏幕。

      # import required libraries
      from vidgear.gears import ScreenGear
      import cv2
      
      # define dimensions of screen w.r.t to given monitor to be captured
      options = {'top': 40, 'left': 0, 'width': 100, 'height': 100}
      
      # open video stream with defined parameters
      stream = ScreenGear(logging=True, **options).start()
      
      # loop over
      while True:
      
          # read frames from stream
          frame = stream.read()
      
          # check for frame if Nonetype
          if frame is None:
              break
      
      
          # {do something with the frame here}
      
      
          # Show output window
          cv2.imshow("Output Frame", frame)
      
          # check for 'q' key if pressed
          key = cv2.waitKey(1) & 0xFF
          if key == ord("q"):
              break
      
      # close output window
      cv2.destroyAllWindows()
      
      # safely close video stream
      stream.stop()
      

      VidGear 库文档: https://abhitronix.github.io/vidgear

      ScreenGear API: https://abhitronix.github.io/vidgear/latest/gears/screengear/overview/

      更多示例: https://abhitronix.github.io/vidgear/latest/gears/screengear/usage/

      【讨论】:

      • 一个问题要问:这个库支持哪些平台?谢谢
      • @PaulLam 所有人。您可以在您选择的任何平台上安装它:abhitronix.github.io/vidgear/latest/installation/…
      • 哦哇,太好了:)感谢您的回复和链接。它还支持Android和iOS等移动操作系统吗?
      • @PaulLam 有人在 android 上尝试过:github.com/rubar-tech/vidgear-kivy-android-opencv 但不确定 iOS。如果你能够在 iOS 上安装和运行 python,那么它是肯定的。
      • 哇,好流畅,我稍后试试:)再次感谢
      猜你喜欢
      • 2016-02-14
      • 1970-01-01
      • 2011-07-19
      • 2011-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-05
      相关资源
      最近更新 更多