【发布时间】:2019-08-09 08:02:28
【问题描述】:
回答编辑:Dima 解决了我的问题,我不必要地调用 cv2.decode 来获取可以直接传递给 OpenCV 的数据。
首先,我对 Python 和 OpenCV 还很陌生,如果我遗漏了一些明显的东西,我深表歉意,我几乎可以肯定我是。我正在尝试使用 Python 从特定窗口中获取屏幕截图,然后将其传递给 OpenCV。如果我将屏幕截图写到磁盘然后读回来,一切都很好,但是因为我想每秒分析多个屏幕截图,等待磁盘 IO 似乎很愚蠢。不幸的是,我花了大约 4 个小时来尝试我想出的所有东西,但没有任何东西可以解决问题。这是我的代码:
from PIL import ImageGrab
from PIL import Image
import win32gui
import win32ui
from ctypes import windll
import os
import time
import cv2
import numpy as np
from matplotlib import pyplot
def enumHandler(hwnd, lParam):
if win32gui.IsWindowVisible(hwnd):
if 'Sword' in win32gui.GetWindowText(hwnd):
lParam.append(hwnd)
def screenGrab(rect):
im = ImageGrab.grab(rect)
im.save(os.getcwd() + '\\game__' + str(int(time.time())) +
'.png', 'PNG')
def main():
hwnds = []
win32gui.EnumWindows(enumHandler, hwnds)
rect = win32gui.GetWindowRect(hwnds[0])
w = rect[2] - rect[0]
h = rect[3] - rect[1]
hwndDC = win32gui.GetWindowDC(hwnds[0])
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
saveDC.SelectObject(saveBitMap)
result = windll.user32.PrintWindow(hwnds[0], saveDC.GetSafeHdc(), 0)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
im.save('thistest.png')
img = cv2.imread('thistest.png', cv2.IMREAD_UNCHANGED)
if img is not None:
print(img.shape)
print(type(img))
cv2.imshow('FRAME', img)
cv2.waitKey()
im2 = np.frombuffer(bmpstr, dtype='uint8')
im2.shape = (bmpinfo['bmHeight'], bmpinfo['bmWidth'], 4)
img2 = cv2.imdecode(im2, cv2.IMREAD_GRAYSCALE)
if img2 is not None:
print(img2.shape)
print(type(img2))
cv2.imshow('FRAME', img2)
cv2.waitKey()
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnds[0], hwndDC)
if __name__ == '__main__':
main()
唯一让我印象深刻的是,如果我打印出 bmpstr 的大小/形状,它会显示类似 1000,1600,3 的内容。但是,在使用 numpy.frombuffer 后,我无法重塑为 1000,1600,3 而是必须使用 4 因为它会抱怨大小。当查看 bmpstr 的 len 时,它会显示 ~6400000,所以数学是有意义的,你需要“4”维度,但 bmpstr 显示与 3 相同的大小。我认为这意味着 bmpstr 以某种方式从 0 开始计数,很好,但为什么可以'我对 numpy.shape/reshape 做同样的事情吗?
无论如何,这是我对出了什么问题的唯一猜测,并且可能完全不符合实际。任何帮助表示赞赏。
编辑:我相信 Dima 是在正确的轨道上指出我需要从 RGB 转换为 BGR。不幸的是,我尝试了多种方法,但仍然无法完成这项工作。这是我当前的代码。如果我取消注释 im.save,则图像写入成功。
from PIL import Image
import win32gui
import win32ui
from ctypes import windll
import os
import time
import cv2
import numpy as np
from matplotlib import pyplot
def enumHandler(hwnd, lParam):
if win32gui.IsWindowVisible(hwnd):
if 'Sword' in win32gui.GetWindowText(hwnd):
lParam.append(hwnd)
def main():
hwnds = []
win32gui.EnumWindows(enumHandler, hwnds)
rect = win32gui.GetWindowRect(hwnds[0])
w = rect[2] - rect[0]
h = rect[3] - rect[1]
hwndDC = win32gui.GetWindowDC(hwnds[0])
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC()
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
saveDC.SelectObject(saveBitMap)
result = windll.user32.PrintWindow(hwnds[0], saveDC.GetSafeHdc(), 0)
if (result):
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
#im.save('thistest.png')
im2 = np.array(im)
#im2 = im2[..., :2]
im2 = im2[:, :, ::-1].copy()
img2 = cv2.imdecode(im2, cv2.IMREAD_UNCHANGED)
if img2 is not None:
cv2.imshow('FRAME', img2)
cv2.waitKey()
else:
print('img2 was empty')
win32gui.DeleteObject(saveBitMap.GetHandle())
saveDC.DeleteDC()
mfcDC.DeleteDC()
win32gui.ReleaseDC(hwnds[0], hwndDC)
if __name__ == '__main__':
main()
【问题讨论】:
-
我认为 Windows 使用 Alpha 保存屏幕转储,因此您可能需要
Image.frombuffer('RGBA',...)来制作 4 个频道 -
不确定您评论的是哪一部分。接收我现有的 Image.frombuffer 的“im”对象工作正常,这是在我解决问题时写入磁盘的对象。来自 cv2.imdecode 的 img2 更进一步是行不通的。我只是尝试获取功能性 im 对象,使用 im.tobytes 输入 cv2.imdecode 以查看是否得到不同的结果,但同样的事情发生了 - cv2.imdecode 返回 None。