用 OpenCV 读取 PyCBitmap

问题描述 投票:0回答:2

我使用 Win32gui 从窗口截图创建了一个图像。该对象具有类型:

object 'PyCBitmap' - assoc is 000002AF9A64DB50, vi=<None>

然后我想将其传递给 OpenCV 进行分析。我使用以下方法成功读取了保存的 .bmp 文件:

cv2.imread(img_file, 0)

尝试对 PyCBitmap 对象使用

cv2.imread
时出现以下错误:

TypeError: bad argument type for built-in operation

我的问题是:

如何将 PyCBitmap 对象转换为

cv2.imread
可接受的类型,而不必先将对象另存为 .bmp 文件?

提前致谢,

贝扎德

p.s 我正在使用带有 python 绑定的 opencv 3.1,我很乐意遵循用 C++ 或 python 编写的建议 :)

python c++ opencv win32gui
2个回答
2
投票

一直在找同样的东西,结合其他几个SO答案终于找到了:

基本上,我想出的代码是:

import PIL, numpy, cv2
bmpinfo = dataBitMap.GetInfo()
bmparray = numpy.asarray(dataBitMap.GetBitmapBits(), dtype=numpy.uint8)
pil_im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmparray, 'raw', 'BGRX', 0, 1)
pil_array = numpy.array(pil_im)
cv_im = cv2.cvtColor(pil_array, cv2.COLOR_RGB2BGR)

简要说明:Python OpenCV 只使用 numpy 数组,所以技巧实际上是将字节转换为正确的 numpy 数组格式。事实证明,为此您需要一个像 PIL 这样的图像处理库,它可以处理特定于图像的逻辑,例如剪切 alpha 通道。输入数据一般是RGBX格式,PIL转成RGB,OpenCV转成自己喜欢的BGR。

我对此进行了分析,不幸的是,它比

GetBitmapBits()
慢得多,并且将其元组结果转换为数组。


0
投票

我知道我来晚了,但也许它会在未来帮助某人,因为这需要很长时间才能找到。这是速度的巨大提升。出于某种原因,numpy.asarray() 和许多其他 numpy 数组创建方法非常慢。我所有的发现主要基于 Adam S 的回答。

线

bmparray = numpy.array(bitmap.GetBitmapBits()).astype(numpy.uint8)

在我的机器上大约需要 0.25 秒,这不是很好。要解决此问题,我们可以避免在此处创建数组。

bmpinfo = bitmap.GetInfo()
bmpbits = bitmap.GetBitmapBits(True)
pil_im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpbits, 'raw', 'BGRX', 0, 1)
pil_array = numpy.array(pil_im)    
img = cv2.cvtColor(pil_array, cv2.COLOR_RGB2BGR)

这是我的代码优化版本,它可以在 1 秒内截取约 55 个屏幕截图,与每秒获取约 6 个屏幕截图的旧方法相对:

import win32gui
import win32ui
import win32con
import numpy, cv2
from PIL import Image

def convert(bitmap) -> cv2.Mat:
    bmpinfo = bitmap.GetInfo()
    bmpbits = bitmap.GetBitmapBits(True)
    pil_im = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpbits, 'raw', 'BGRX', 0, 1)
    pil_array = numpy.array(pil_im)    
    return cv2.cvtColor(pil_array, cv2.COLOR_RGB2BGR)


def take_screen_shot():
    # https://stackoverflow.com/questions/3586046/fastest-way-to-take-a-screenshot-with-python-on-windows
    w = 1920 # set this
    h = 1080 # set this
    
    hwnd = win32gui.FindWindow(None, "co-op games.txt - Notepad")
    wDC = win32gui.GetWindowDC(hwnd)
    dcObj=win32ui.CreateDCFromHandle(wDC)
    cDC=dcObj.CreateCompatibleDC()
    dataBitMap = win32ui.CreateBitmap()
    dataBitMap.CreateCompatibleBitmap(dcObj, w, h)
    cDC.SelectObject(dataBitMap)
    cDC.BitBlt((0,0),(w, h) , dcObj, (0,0), win32con.SRCCOPY)
    
    image = convert(dataBitMap)

    # Free Resources
    dcObj.DeleteDC()
    cDC.DeleteDC()
    win32gui.ReleaseDC(hwnd, wDC)
    win32gui.DeleteObject(dataBitMap.GetHandle())
    return image
© www.soinside.com 2019 - 2024. All rights reserved.