English | 简体中文
🚀 为 Python 自动化而生的窗口捕获库
180+ FPS 极致性能 · 零资源待机 · 无视遮挡 · API 极简
你是否在为以下问题困扰?
- mss/BitBlt:无法捕获被遮挡或后台窗口
- PrintWindow:性能瓶颈,固定 26ms+ 延迟
- 其他 WGC 封装:持续运行占用资源,频繁启停开销巨大(50ms+)
wgc_python 通过独创的 Pause/Resume 机制,完美解决了这个矛盾:
# 传统方式:要么持续空转浪费资源,要么频繁启停承受延迟
start_capture() # 50ms 开销
get_frame() # 获取截图
stop_capture() # 销毁会话(50ms)
# 下次截图又要重新开始...
# wgc_python 方式:一次启动,按需截图,零开销待机
with WindowCapture("窗口", "类名") as cap:
while running:
frame = cap.capture_one() # auto Resume → 等待帧 → 拷贝 → Pause
# 处理图像...| 方案 | FPS | 后台捕获 | CPU 占用 | 频繁切换开销 | 暂停后 GPU 占用 |
|---|---|---|---|---|---|
| python-mss / BitBlt | ~60 | ❌ | 高 | 低 | N/A (无暂停概念) |
| PrintWindow | ~38 | ✅ | 中 | 低 | N/A (每次调用即捕获) |
| 其他 WGC 封装 | 180+ | ✅ | 高(持续空转) | 极高 (50ms+) | 高 (无法真正暂停) |
| wgc_python | 180+ | ✅ | 极低(Pause时归零) | <1μs(原子标志位) | 归零(无 D3D 操作) |
- 180+ FPS 高帧率捕获,比 PrintWindow 快 5 倍
- 双缓冲 Staging 纹理:GPU 异步拷贝,读写互不阻塞
- 零拷贝友好:
np.ndarray(strides=...)直接从 GPU 映射内存构造视图
- Pause/Resume <1μs 软暂停:不销毁不重建 WGC session,仅原子标志位跳过帧处理
- capture_one() 自动管理:Resume → 等待帧 → 拷贝 → Pause,间隙 GPU 驱动零开销
- 会话复用:避免频繁创建/销毁 D3D 设备的开销
- capture_one():一行代码完成按需捕获,返回 numpy 数组
- get_frame():零拷贝裸指针路径(高级使用)
- 线程安全:C++ 层处理所有多线程复杂性
- 同一进程内可同时创建多个捕获会话,互不干扰
- 每个会话独立 D3D11 设备 + 独立纹理 + 独立 WinRT session,完全隔离
- 支持同窗口多路并发捕获
- 默认
client_area_only=True:只捕获窗口客户区内容,自动裁剪标题栏和边框,直接输出有效像素 - 设置
client_area_only=False:捕获整个窗口(含标题栏和边框),满足 UI 记录场景 - DPI 感知:自动修正高 DPI 缩放偏移,裁剪精度像素级
- GPU 级裁剪:
CopySubresourceRegion在 GPU 上完成裁剪,不浪费带宽和 CPU
- 支持捕获被遮挡、最小化、后台窗口
- 完美适配游戏、桌面应用等各种场景
pip install wgc-pythonfrom wgc_python import WindowCapture, enumerate_windows
# 枚举所有窗口
for title, class_name in enumerate_windows():
print(f"{title} ({class_name})")
# 按需捕获(推荐 —— 零开销待机)
with WindowCapture("窗口标题", "窗口类名") as cap:
frame = cap.capture_one() # BGRA numpy 数组,shape (h, w, 4)
if frame is not None:
print(f"捕获成功: {frame.shape}")
# 客户区裁剪演示
# 默认 client_area_only=True:只截取客户区,不含标题栏/边框
cap_client = WindowCapture("记事本", "Notepad") # 只截内容
cap_full = WindowCapture("记事本", "Notepad", client_area_only=False) # 含标题栏
frame_client = cap_client.capture_one() # 只有编辑区
frame_full = cap_full.capture_one() # 含标题栏 + 菜单 + 编辑区
cap_client.close()
cap_full.close()from wgc_python import WindowCapture
cap = WindowCapture("游戏窗口", "UnityWndClass")
while True:
frame = cap.capture_one(timeout=1.0)
if frame is not None:
# frame 是 BGRA numpy 数组,直接用于 OpenCV/模板匹配
pass
time.sleep(1)
cap.close()from wgc_python import WindowCapture
import numpy as np
import ctypes
with WindowCapture("窗口", "类名") as cap:
cap.resume()
r = cap.get_frame() # (ptr, w, h, row_pitch) — GPU 映射裸指针
if r:
ptr, w, h, rp = r
arr = np.ndarray((h, w, 4), dtype=np.uint8,
buffer=(ctypes.c_ubyte * (h * rp)).from_address(ptr),
strides=(rp, 4, 1))
# arr 是 GPU 内存的零拷贝视图
cap.release_frame()
cap.pause()from wgc_python import WindowCapture
import cv2
with WindowCapture("窗口标题", "窗口类名") as cap:
while True:
frame = cap.capture_one()
if frame is not None:
cv2.imshow("Capture", cv2.cvtColor(frame, cv2.COLOR_BGRA2BGR))
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()from wgc_python import (
WindowCapture, # 窗口捕获类(上下文管理器支持)
enumerate_windows, # 枚举所有可见窗口
get_last_error, # 获取最后错误信息(线程安全)
get_active_capture_count, # 获取活跃捕获数
)
# WindowCapture 类方法:
# cap = WindowCapture(title, class_name, client_area_only=True)
#
# cap.capture_one(timeout=0.5) -> np.ndarray | None ★ 推荐
# 自动 Resume → 等待帧 → 拷贝为 numpy → Pause
# 捕获间隙 WGC 完全休眠,GPU 驱动零开销
#
# cap.get_frame() -> (ptr, w, h, row_pitch) | None
# cap.release_frame() # 释放 GPU 映射
# cap.pause() # 暂停捕获(零资源待机)
# cap.resume() # 恢复捕获
# cap.stop() # 停止帧到达
# cap.close() # 销毁会话
# cap.is_capturing() -> bool
# cap.is_paused() -> bool
# cap.get_frame_count() -> int
# cap.handle -> int (DLL handle)WGC捕获 → GPU Surface纹理
│
┌────────▼────────┐
│ FrameArrived │
│ if pausing → ↑ │ ← Pause时直接返回,零 D3D 操作
└────────┬─────────┘
│
CopyResource (GPU异步复制)
↓
┌─────────────────────────┐
│ 双缓冲Staging纹理 │
│ [0] 写入 ←→ [1] 读取 │
│ m_textureInUse 防冲撞 │
└─────────────────────────┘
↓
Map (永久映射 GPU 内存)
↓
┌────── 零拷贝输出 ───────┐
│ get_frame() │
│ 返回裸指针 → numpy零拷贝 │
│ 需手动 release_frame() │
└──────────────────────────┘
┌────── 一键捕获 ──────────┐
│ capture_one() │
│ auto Pause/Resume │
│ 返回 numpy 数组 │
│ 间隙 GPU 驱动零开销 │
└──────────────────────────┘
用户调用 cap.pause()
│
m_isPaused = true ◄──── 原子标志位,<1μs
m_readableStagingIndex = -1
│
┌────▼────────────────────────────────────────────┐
│ FrameArrived 回调(WGC 仍会触发) │
│ │
│ lock(mutex); │
│ if (m_isPaused) return; // ← 纯CPU判断,跳过│
│ // ↓ 以下只在 resume 后执行 ↓ │
│ CopyResource(staging, frame); │
│ m_readableStagingIndex = idx; │
│ unlock(mutex); │
└────▲────────────────┬───────────────────────────┘
│ │
用户调用 cap.resume() MapFrame 检查 readableStagingIndex
m_isPaused = false <0 → 最近帧尚未就绪,返回 false
不销毁 WGC session / 不重建 D3D 设备 / 不重新注册回调
→ 恢复零延迟,无突刺
wgc_python/
├── wgc_python/ # Python 包
│ ├── __init__.py # Python API(ctypes FFI)
│ └── wgc_python.dll # 编译后的 DLL
├── wgc_python_dll/ # C++ DLL 项目
│ ├── WGCWindowCapture.h/cpp # WGC 捕获核心(双缓冲 + 零拷贝)
│ ├── WGCExport.h/cpp # DLL 导出(含线程安全错误处理)
│ ├── D3DInterop.cpp # D3D11 设备互操作
│ ├── WindowEnumerator.h/cpp # 窗口枚举
│ ├── pch.h # 预编译头
│ └── packages/ # NuGet 包
├── test.py # 功能测试
├── test_mt.py # 多线程按需捕获示例
├── pyproject.toml # pip 构建配置
├── BUILD.md / BUILD_EN.md # 构建说明(中/英)
├── README.md / README_EN.md # 使用文档(中/英)
├── CONTRIBUTING.md # 贡献指南
├── CODE_OF_CONDUCT.md # 行为准则
├── LICENSE # MIT 许可证
└── requirements.txt # Python 依赖
- Windows 10 1903+ (Build 18362)
- Python 3.6+
详见 BUILD.md
| 问题 | 解决方案 |
|---|---|
| DLL 未找到 | 确保 wgc_python.dll 在正确位置 |
| 捕获失败 | 检查窗口是否可见,Windows 版本 >= 1903 |
| 中文路径保存失败 | 使用 cv2.imencode + open().write() 代替 cv2.imwrite |
| 依赖缺失 | pip install numpy opencv-python |
- ✅ 游戏 AI / 自动化脚本
- ✅ RPA 流程自动化
- ✅ 屏幕录制 / 直播
- ✅ UI 自动化测试
- ✅ 计算机视觉应用
本项目基于 robmikh/Win32CaptureSample 开发。
MIT License