【问题标题】:Taking fast screenshot Winapi and Opencv快速截屏 Winapi 和 Opencv
【发布时间】:2017-02-27 03:08:52
【问题描述】:

我需要为我正在处理的 OpenCV 项目快速截取游戏截图。例如,我可以轻松使用 PIL:

def take_screenshot1(hwnd):
    rect = win32gui.GetWindowRect(hwnd)
    img = ImageGrab.grab(bbox=rect)
    img_np = np.array(img)
    return cv2.cvtColor(img_np, cv2.COLOR_RGB2BGR)

但平均需要 0.05 秒,这对于实时捕捉来说不够快。

我可以使用here 发布的答案,但这只会将位图保存到文件中。这比使用 PIL 快 10 倍以上,但我不确定 OpenCV 中有什么方法可以将其转换为 bgr/hsv 图像。

def take_screenshot(hwnd):
    wDC = win32gui.GetWindowDC(hwnd)
    dcObj=win32ui.CreateDCFromHandle(wDC)
    cDC=dcObj.CreateCompatibleDC()
    dataBitMap = win32ui.CreateBitmap()
    dataBitMap.CreateCompatibleBitmap(dcObj, 500, 500)
    cDC.SelectObject(dataBitMap)
    cDC.BitBlt((0, 0), (500, 500), dcObj, (0, 0), win32con.SRCCOPY)

    dataBitMap.SaveBitmapFile(cDC, "foo.png")

    dcObj.DeleteDC()
    cDC.DeleteDC()
    win32gui.ReleaseDC(hwnd, wDC)
    win32gui.DeleteObject(dataBitMap.GetHandle())
    im = cv2.imread("foo.png")
    return cv2.cvtColor(im, cv2.COLOR_RGB2BGR)

编辑:窗口的大小为 500x500,因此在两个示例中它保存的区域相同。

即使我保存图像,然后用 OpenCV 重新打开它,它仍然比 PIL 快,但肯定有更简单的方法吗?

编辑:好的,所以使用 cmets 并对 winapi 进行一些研究,我现在可以直接访问位图数据,如下所示:

def take_screenshot1(hwnd):
wDC = win32gui.GetWindowDC(hwnd)
dcObj=win32ui.CreateDCFromHandle(wDC)
cDC=dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, 500, 500)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0, 0), (500, 500), dcObj, (0, 0), win32con.SRCCOPY)

im = dataBitMap.GetBitmapBits(True)  # Tried False also
img = np.array(im)
cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
print(img)

dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())

但我不确定如何将返回的位图转换为 OpenCV 可以理解的形式,因为在 OpenCV 中没有将位图转换为 rgb/bgr 的方法

【问题讨论】:

  • 在第一个版本中,您正在阅读窗口的整个区域。在第二个版本中,您正在阅读 500x500 像素区域。除非两个区域相同,否则您无法比较速度。 BitBlt 有一个瓶颈,你真的无能为力。
  • @Barmak Shermirana Ahh 我应该指定得更好。我正在捕获的窗口是 500x500,所以大小是相同的。所以我想我可能只需要保存并打开看起来的 img ..
  • 您不必保存图像数据。只需在 dataBitMap 上调用GetObject,您就可以通过返回的BITMAP 结构直接访问像素数据。明显的优化包括: 在截图功能之外创建位图和 DC。而且还不完全清楚,为什么你打电话给CreateBitmap,然后是CreateCompatibleBitmap。我不明白,为什么需要前者。
  • @IInspectable 如果我调用:win32gui.GetObject(dataBitMap) 我收到错误“TypeError: The object is not a PyHANDLE object”
  • 那是错误的GetObject 电话。我指的是 GDI 功能(如文档所述)。作为替代方案,如果您想存储位图数据的副本,您可以调用GetDIBits。您使用的所有操作都需要对Windows GDI 有一定的了解。

标签: python opencv numpy winapi


【解决方案1】:

我只会展示适合我的代码。

import time
import win32gui
import win32ui
import win32con
import win32api
import numpy as np
import cv2


def window_capture():
    hwnd = 0
    hwndDC = win32gui.GetWindowDC(hwnd)
    mfcDC = win32ui.CreateDCFromHandle(hwndDC)
    saveDC = mfcDC.CreateCompatibleDC()
    saveBitMap = win32ui.CreateBitmap()

    MoniterDev = win32api.EnumDisplayMonitors(None, None)
    w = MoniterDev[0][2][2]
    h = MoniterDev[0][2][3]

    saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
    saveDC.SelectObject(saveBitMap)
    saveDC.BitBlt((0, 0), (w, h), mfcDC, (0, 0), win32con.SRCCOPY)
    im = saveBitMap.GetBitmapBits(True)  # Tried False also
    img = np.frombuffer(im, dtype=np.uint8).reshape((h, w, 4))

    cv2.imshow("demo", img)
    cv2.waitKey(100)


beg = time.time()
for i in range(100):
    window_capture()
end = time.time()
print(end - beg)

cv2.destroyAllWindows()

【讨论】:

  • 您能否向我们解释一下这段代码是如何解决问题的?
猜你喜欢
  • 2015-12-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-24
  • 2014-09-19
  • 2018-11-13
相关资源
最近更新 更多