【问题标题】:OpenCV: imdecode image from memory returns NoneOpenCV:从内存中解码图像返回无
【发布时间】: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。

标签: python opencv


【解决方案1】:

只要你有im,它的类型是PIL.Image,你就可以立即将它转换为numpy,从而像这样的OpenCV

img = np.array(im)
# In case it is needed to get rid of alpha channel, if it is present
img = img[..., :2]
# To convert RGB to BGR 
img = img[:, :, ::-1].copy()
cv2.imshow('FRAME', img)
cv2.waitKey()

一般来说,使用PIL对图像进行解码,然后使用OpenCV对其进行处理是一种常见的做法。

【讨论】:

  • 我觉得从 RGB 到 BGR 的转换是我所缺少的,但不幸的是我无法从您的示例中得到任何结果。我用我当前的代码更新了我的问题。
  • 我已经更新了我的代码来说明你可以直接使用这张图片进行imshow
  • 感谢您的帮助,我没有意识到可以跳过解码。根据您的示例,我现在可以正常工作了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-03
  • 2012-11-14
  • 2011-07-21
  • 2013-09-14
  • 2014-06-04
  • 1970-01-01
  • 2016-02-18
相关资源
最近更新 更多