全屏游戏截图失败:修改numpy dtype后触发ValueError报错求助
全屏游戏截图黑屏+数组重塑报错解决方法
一、当前报错的原因及修复
你遇到的ValueError: cannot reshape array of size 12288000 into shape (1600,3840,4)是因为:
- 原代码用
numpy.uint8时,bitmap.GetBitmapBits(True)返回的是32位BGRA位图的字节数据(每个像素4字节),数组元素数正好是高度×宽度×4,所以重塑形状没问题。 - 改成
numpy.uint16后,每个数组元素占2字节,总元素数变成原来的一半(1600×3840×4 ÷2 =12288000),这时候再强行设置形状为(高度,宽度,4),元素总数不匹配,自然报错。
如果非要用uint16读取,修复代码如下:
output = numpy.frombuffer(bitmap.GetBitmapBits(True), dtype = numpy.uint16) output.shape = (height, width, 2) # 每个像素对应2个uint16元素 # 转换回uint8的BGRA格式 output = output.view(numpy.uint8).reshape(height, width, 4)
但这只是修复报错,解决不了全屏游戏黑屏的根本问题。
二、全屏游戏黑屏的根本原因
win32gui的BitBlt只能捕获GDI绘制的普通窗口画面,而现代全屏游戏大多用Direct3D/OpenGL做硬件加速渲染,画面直接存在GPU显存里,BitBlt无法读取这部分内容,所以返回黑屏。
三、解决全屏游戏截图的正确方案
改用DXGI API捕获画面,它能直接读取GPU显存中的渲染内容,支持硬件加速窗口的截图。以下是完整实现代码:
import cv2 as cv import numpy as np import win32gui import win32con from ctypes import windll, byref, c_void_p # 初始化DXGI相关函数与常量 dxgi = windll.dxgi d3d11 = windll.d3d11 class DXGIScreenCapture: def __init__(self, hwnd): self.hwnd = hwnd # 获取窗口尺寸 left, top, right, bottom = win32gui.GetWindowRect(hwnd) self.width = right - left self.height = bottom - top # 创建D3D11设备与上下文 self._create_d3d_device() # 获取DXGI输出复制对象 self._create_output_duplication() def _create_d3d_device(self): self.device = c_void_p() self.context = c_void_p() d3d11.D3D11CreateDevice( None, win32con.D3D_DRIVER_TYPE_HARDWARE, None, 0, None, 0, win32con.D3D11_SDK_VERSION, byref(self.device), None, byref(self.context) ) # 获取DXGI适配器 self.adapter = c_void_p() self.device.QueryInterface(dxgi.IDXGIAdapter, byref(self.adapter)) # 获取DXGI输出 self.output = c_void_p() self.adapter.EnumOutputs(0, byref(self.output)) def _create_output_duplication(self): self.output_dup = c_void_p() self.output.DuplicateOutput(self.device, byref(self.output_dup)) def capture(self): frame_info = dxgi.DXGI_OUTDUPL_FRAME_INFO() resource = c_void_p() # 获取下一帧 self.output_dup.AcquireNextFrame(0, byref(frame_info), byref(resource)) # 创建 staging 纹理用于CPU读取 tex_desc = dxgi.DXGI_TEXTURE2D_DESC() tex_desc.Width = self.width tex_desc.Height = self.height tex_desc.MipLevels = 1 tex_desc.ArraySize = 1 tex_desc.Format = win32con.DXGI_FORMAT_B8G8R8A8_UNORM tex_desc.SampleDesc.Count = 1 tex_desc.SampleDesc.Quality = 0 tex_desc.Usage = dxgi.DXGI_USAGE_STAGING tex_desc.CPUAccessFlags = dxgi.DXGI_CPU_ACCESS_READ tex_desc.BindFlags = 0 tex_desc.MiscFlags = 0 staging_tex = c_void_p() self.device.CreateTexture2D(byref(tex_desc), None, byref(staging_tex)) # 复制帧资源到staging纹理 self.context.CopyResource(staging_tex, resource) # 映射纹理到CPU内存 mapped_rect = dxgi.DXGI_MAPPED_RECT() self.context.Map(staging_tex, 0, dxgi.DXGI_MAP_READ, 0, byref(mapped_rect)) # 转换为numpy数组并调整格式 img = np.frombuffer(mapped_rect.pBits, dtype=np.uint8).reshape(self.height, self.width, 4) img = cv.cvtColor(img, cv.COLOR_BGRA2BGR) # 转为OpenCV支持的BGR格式 # 释放资源 self.context.Unmap(staging_tex, 0) self.output_dup.ReleaseFrame() resource.Release() staging_tex.Release() return img def get_window_handle(title_part): target_hwnd = None def enum_windows_callback(handle, extra): nonlocal target_hwnd if title_part in win32gui.GetWindowText(handle): target_hwnd = handle return False return True win32gui.EnumWindows(enum_windows_callback, None) return target_hwnd # 使用示例 hwnd = get_window_handle("Marvel's Spider-Man Remastered") if hwnd: capturer = DXGIScreenCapture(hwnd) screenshot = capturer.capture() cv.imshow('Game Screenshot', screenshot) cv.waitKey(0) cv.destroyAllWindows() else: print("未找到目标游戏窗口")
四、注意事项
- 运行代码需要安装
pywin32和opencv-python依赖:pip install pywin32 opencv-python - 部分游戏可能有反作弊机制,会阻止DXGI截图,这种情况下需要调整游戏的反作弊设置或使用其他兼容方案。
内容的提问来源于stack exchange,提问作者phpjunkie
相关产品推荐
相关产品推荐

