此问题在此处已有答案:
Capture video data from screen in Python(11个答案)
6天前关闭。
这篇文章是6天前编辑并提交审查的。
我正在学习Open CV的教程,并尝试重写以下代码:https://github.com/learncodebygaming/opencv_tutorials/tree/master/005_real_time
(具体来说,就是windowcapture.py文件)
该文件使用win32gui、win32ui、win32con按窗口名捕获给定的打开窗口,并对它进行屏幕截图,以便稍后进行cv2处理。
我曾尝试使用Quartz for macOS重新创建此功能,示例如下:https://stackoverflow.com/a/48030215/14649706
我自己的www.example.com版本windowcapture.py是这样的:
import numpy as np
from Quartz import CGWindowListCopyWindowInfo, kCGNullWindowID, kCGWindowListOptionAll, CGRectNull, CGWindowListCreateImage, kCGWindowImageBoundsIgnoreFraming, kCGWindowListExcludeDesktopElements, CGImageGetDataProvider, CGDataProviderCopyData, CFDataGetBytePtr, CFDataGetLength
import os
from PIL import Image
import cv2 as cv
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()
# determine the window we want to capture
def get_window(self):
windows = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, 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):
image_filename = 'test-img.png'
# -x mutes sound and -l specifies windowId
os.system('screencapture -x -l %s %s' % (self.window_id, image_filename))
pil_image = Image.open(image_filename)
image_as_numpy_array = np.array(pil_image)
os.remove(image_filename)
image = cv.cvtColor(image_as_numpy_array, cv.COLOR_BGR2RGB)
return image
这里我的get_image_from_window
方法工作正常,我可以使用cv.imshow('cv', screenshot)
查看它:
import cv2 as cv
from time import time
from windowcapture import WindowCapture
# initialize the WindowCapture class
wincap = WindowCapture('Blue Box Clicker')
loop_time = time()
while(True):
# get an updated image of the game
screenshot = wincap.get_image_from_window()
cv.imshow('cv', screenshot)
# debug the loop rate
print('FPS {}'.format(1 / (time() - loop_time)))
loop_time = time()
# press '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.')
但是我不想在本地保存图像然后再加载它,我认为这是非常低效的,我想实现同样的功能,而不是实际保存图像文件,然后打开它。
类似于这里的做法(在上面的GitHub链接中):
def get_screenshot(self):
# get the window image data
wDC = win32gui.GetWindowDC(self.hwnd)
dcObj = win32ui.CreateDCFromHandle(wDC)
cDC = dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, self.w, self.h)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (self.w, self.h), dcObj, (self.cropped_x, self.cropped_y), win32con.SRCCOPY)
# convert the raw data into a format opencv can read
#dataBitMap.SaveBitmapFile(cDC, 'debug.bmp')
signedIntsArray = dataBitMap.GetBitmapBits(True)
img = np.fromstring(signedIntsArray, dtype='uint8')
img.shape = (self.h, self.w, 4)
# free resources
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(self.hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())
# drop the alpha channel, or cv.matchTemplate() will throw an error like:
# error: (-215:Assertion failed) (depth == CV_8U || depth == CV_32F) && type == _templ.type()
# && _img.dims() <= 2 in function 'cv::matchTemplate'
img = img[...,:3]
# make image C_CONTIGUOUS to avoid errors that look like:
# File ... in draw_rectangles
# TypeError: an integer is required (got type tuple)
# see the discussion here:
# https://github.com/opencv/opencv/issues/14866#issuecomment-580207109
img = np.ascontiguousarray(img)
return img
如何使用Quartz实现这一点?
我在MacOS(M1 Pro)上,真的很想让这个工作。
目前,这个程序运行在12fps左右。
它试图捕获的程序是另一个python程序(一个简单的pygame):
import pygame
import random
# Set up the game window
pygame.init()
window_width, window_height = 640, 480
window = pygame.display.set_mode((window_width, window_height))
pygame.display.set_caption("Blue Box Clicker")
# Set up the clock
clock = pygame.time.Clock()
# Set up the game variables
background_color = (0, 0, 0)
box_color = (0, 0, 255)
box_width, box_height = 50, 50
box_x, box_y = 0, 0
# Set up the game loop
running = True
while running:
# Handle events
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = pygame.mouse.get_pos()
if box_x <= mouse_x <= box_x + box_width and box_y <= mouse_y <= box_y + box_height:
# Correct click
box_x, box_y = random.randint(
0, window_width - box_width), random.randint(0, window_height - box_height)
# Incorrect click
# Draw the background
window.fill(background_color)
# Draw the box
pygame.draw.rect(window, box_color, (box_x, box_y, box_width, box_height))
# Update the window
pygame.display.update()
# Limit the frame rate
clock.tick(60)
# Clean up
pygame.quit()
1条答案
按热度按时间8cdiaqws1#
我使用下面的代码修复了这个问题:
此方法以
cv2
可以识别并用于matchTemplate
的格式返回捕获的CGImage。我的完整代码如下所示:
x一个一个一个一个x一个一个二个x
而这在MacBook Pro(配有M1 Pro处理器)上的平均60fps下也能正常工作。