有没有一种使用Python(可能是通过OpenCV或PIL)连续抓取整个屏幕或部分屏幕帧的方法,至少以15fps或更高的速度?我已经看到其他语言实现过这个功能,所以理论上应该是可能的。
我不需要将图像数据保存到文件中。实际上,我只想输出包含原始RGB数据的数组(例如numpy数组),因为我打算将其发送到一个大型LED显示屏(可能在重新调整大小后)。
有没有一种使用Python(可能是通过OpenCV或PIL)连续抓取整个屏幕或部分屏幕帧的方法,至少以15fps或更高的速度?我已经看到其他语言实现过这个功能,所以理论上应该是可能的。
我不需要将图像数据保存到文件中。实际上,我只想输出包含原始RGB数据的数组(例如numpy数组),因为我打算将其发送到一个大型LED显示屏(可能在重新调整大小后)。
尝试了以上所有解决方案后,我无法获得可用的帧率,直到我按以下方式修改了我的代码:
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+帧/秒。
使用 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
pip uninstall mss
,pip install mss==2.0.22
- Nicolas Forstnermss().grab(window)
。代码类似于这样:
screenshot = mss.mss().grab(window)
img = Image.frombytes("RGB", (screenshot.width, screenshot.height), screenshot.rgb)
- qdtroemnerfrom 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()
希望有所帮助。ImageGrab.grab()
,根据我的经验来看非常非常慢(在我的2015款Macbook Pro上大约只有2帧每秒)。这个解决方案无法达到OP要求的超过15帧每秒的捕获速度。我还尝试过更快的 mss
库,但是我没能保证稳定的帧率或图像之间的规则时间间隔。 - Rayimport 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()
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
请确保已安装以下软件包:
Pillow,opencv-python,numpy,mss
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)
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
如果有人正在寻找一种更简单和更快的方法来在Python中抓取屏幕作为帧,那么可以尝试使用ScreenGear
API,它是我高性能视频处理vidgear库中的一部分,只需几行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/
vidgear
,但在导入vidgear
的依赖时出现了一些问题:(第一个是'mss',我通过import mss; mss.mss(display = ":0")
来解决它,然后又出现了另一个错误:x11库未找到
。我尝试在buildozer.spec
的requirement
中包含python3-xlib
,但似乎不起作用:( 我该如何解决它? - Paul Lambuildozer
构建apk
,你是指要编辑源代码吗? 我尝试在我的.py
文件中测试使用 try:from vidgear.gears import ScreenGear;ScreenGear()
,但又出现了另一个错误:(无法在此系统上抓取任何实例,您是否正在无头运行?
我找不到任何在线解决方案.... 你介意打开聊天吗? 我确实需要帮助.... 谢谢。 - Paul Lam我尝试了来自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
import numpy as np
import Quartz as QZ
class WindowCapture:
# properties
window_name = None
window = None
window_id = None
window_width = 0
window_height = 0
# constructor
def __init__(self, given_window_name=None):
if given_window_name is not None:
self.window_name = given_window_name
self.window = self.get_window()
if self.window is None:
raise Exception('Unable to find window: {}'.format(given_window_name))
self.window_id = self.get_window_id()
self.window_width = self.get_window_width()
self.window_height = self.get_window_height()
self.window_x = self.get_window_pos_x()
self.window_y = self.get_window_pos_y()
else:
raise Exception('No window name given')
def get_window(self):
windows = QZ.CGWindowListCopyWindowInfo(QZ.kCGWindowListOptionAll, QZ.kCGNullWindowID)
for window in windows:
name = window.get('kCGWindowName', 'Unknown')
if name and self.window_name in name:
return window
return None
def get_window_id(self):
return self.window['kCGWindowNumber']
def get_window_width(self):
return int(self.window['kCGWindowBounds']['Width'])
def get_window_height(self):
return int(self.window['kCGWindowBounds']['Height'])
def get_window_pos_x(self):
return int(self.window['kCGWindowBounds']['X'])
def get_window_pos_y(self):
return int(self.window['kCGWindowBounds']['Y'])
def get_image_from_window(self):
core_graphics_image = QZ.CGWindowListCreateImage(
QZ.CGRectNull,
QZ.kCGWindowListOptionIncludingWindow,
self.window_id,
QZ.kCGWindowImageBoundsIgnoreFraming | QZ.kCGWindowImageNominalResolution
)
bytes_per_row = QZ.CGImageGetBytesPerRow(core_graphics_image)
width = QZ.CGImageGetWidth(core_graphics_image)
height = QZ.CGImageGetHeight(core_graphics_image)
core_graphics_data_provider = QZ.CGImageGetDataProvider(core_graphics_image)
core_graphics_data = QZ.CGDataProviderCopyData(core_graphics_data_provider)
np_raw_data = np.frombuffer(core_graphics_data, dtype=np.uint8)
numpy_data = np.lib.stride_tricks.as_strided(np_raw_data,
shape=(height, width, 3),
strides=(bytes_per_row, 4, 1),
writeable=False)
final_output = np.ascontiguousarray(numpy_data, dtype=np.uint8)
return final_output
这是一个如何使用它的例子:
import cv2 as cv
from time import time
from windowcapture import WindowCapture
# initialise the WindowCapture class
wincap = WindowCapture('Name of my application window')
loop_time = time()
while(True):
# get an updated image of the window you want
screenshot = wincap.get_image_from_window()
# show that image
cv.imshow('Computer Vision', screenshot)
print('FPS {:.2f}'.format(round(1 / (time() - loop_time), 2)))
loop_time = time()
# hold 'q' with the output window focused to exit.
# waits 1 ms every loop to process key presses
if cv.waitKey(1) == ord('q'):
cv.destroyAllWindows()
break
print('Done.')