English | ็ฎไฝไธญๆ
๐ Window Capture Library Built for Python Automation
180+ FPS Performance ยท Zero-Resource Standby ยท Capture Behind Windows ยท Minimalist API
Are you struggling with these problems?
- mss/BitBlt: Cannot capture occluded or background windows
- PrintWindow: Performance bottleneck, fixed 26ms+ latency
- Other WGC wrappers: Continuous resource consumption, huge overhead for frequent start/stop (50ms+)
wgc_python solves this dilemma with its innovative Pause/Resume mechanism:
# Traditional approach: Either waste resources or suffer latency
start_capture() # 50ms overhead
get_frame() # Get screenshot
stop_capture() # Destroy session (50ms)
# wgc_python approach: One-time init, on-demand capture, zero-overhead standby
with WindowCapture("Window", "Class") as cap:
while running:
frame = cap.capture_one() # auto Resume โ wait โ copy โ Pause
# Process image...| Solution | FPS | Background Capture | CPU Usage | Toggle Overhead | GPU When Paused |
|---|---|---|---|---|---|
| python-mss / BitBlt | ~60 | โ | High | Low | N/A (no pause) |
| PrintWindow | ~38 | โ | Medium | Low | N/A (per-call) |
| Other WGC wrappers | 180+ | โ | High (continuous) | Very High (50ms+) | High (can't truly pause) |
| wgc_python | 180+ | โ | Near Zero (when paused) | <1ฮผs (atomic flag) | Zero (no D3D ops) |
- 180+ FPS high frame rate capture, 5x faster than PrintWindow
- Double-buffered Staging Texture: GPU async copy, read/write non-blocking
- Zero-copy path:
np.ndarray(strides=...)directly from GPU-mapped memory
- Pause/Resume <1ฮผs soft-pause: Atomic flag only, no WGC session teardown
- capture_one() auto management: Resume โ wait โ copy โ Pause, zero GPU driver overhead between captures
- Session Reuse: No frequent D3D device creation/destruction
- capture_one(): One-line on-demand capture, returns numpy array
- get_frame(): Zero-copy raw pointer path (advanced)
- Thread safe: C++ handles all multi-threading complexity
- Create multiple capture sessions simultaneously within one process
- Each session has its own D3D11 device, staging textures, and WinRT session โ fully isolated
- Supports concurrent capture of the same window
- Default
client_area_only=True: captures only window client area, automatically crops title bar and borders - Set
client_area_only=False: captures entire window including title bar and borders, for UI recording - DPI-aware: automatic high-DPI scaling correction for pixel-perfect cropping
- GPU-level cropping via
CopySubresourceRegion, no wasted bandwidth or CPU
- Supports capturing occluded, minimized, and background windows
- Perfect for games, desktop apps, and various scenarios
pip install wgc-pythonfrom wgc_python import WindowCapture, enumerate_windows
# Enumerate all windows
for title, class_name in enumerate_windows():
print(f"{title} ({class_name})")
# On-demand capture (recommended โ zero-resource standby)
with WindowCapture("Window Title", "WindowClass") as cap:
frame = cap.capture_one() # BGRA numpy array, shape (h, w, 4)
if frame is not None:
print(f"Captured: {frame.shape}")
# Client area demo
# Default client_area_only=True: content only, no title bar/borders
cap_client = WindowCapture("Notepad", "Notepad") # content only
cap_full = WindowCapture("Notepad", "Notepad", client_area_only=False) # with title bar
frame_client = cap_client.capture_one() # edit area only
frame_full = cap_full.capture_one() # title bar + menu + edit area
cap_client.close()
cap_full.close()from wgc_python import WindowCapture
cap = WindowCapture("Game Window", "UnityWndClass")
while True:
frame = cap.capture_one(timeout=1.0)
if frame is not None:
# frame is BGRA numpy array, ready for OpenCV/template matching
pass
time.sleep(1)
cap.close()from wgc_python import WindowCapture
import numpy as np
import ctypes
with WindowCapture("Window", "Class") as cap:
cap.resume()
r = cap.get_frame() # (ptr, w, h, row_pitch) โ GPU mapped pointer
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 is a zero-copy view into GPU-mapped memory
cap.release_frame()
cap.pause()from wgc_python import WindowCapture
import cv2
with WindowCapture("Window Title", "WindowClass") 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, # Window capture class (context manager support)
enumerate_windows, # Enumerate all visible windows
get_last_error, # Get last error message (thread-safe)
get_active_capture_count, # Get active capture count
)
# WindowCapture methods:
# cap = WindowCapture(title, class_name, client_area_only=True)
#
# cap.capture_one(timeout=0.5) -> np.ndarray | None โ
recommended
# Auto Resume โ wait for frame โ copy to numpy โ Pause
# WGC fully dormant between captures, zero GPU driver overhead
#
# cap.get_frame() -> (ptr, w, h, row_pitch) | None
# cap.release_frame() # Release GPU mapping
# cap.pause() # Pause capture (zero-resource standby)
# cap.resume() # Resume capture
# cap.stop() # Stop frame arrival
# cap.close() # Destroy session
# cap.is_capturing() -> bool
# cap.is_paused() -> bool
# cap.get_frame_count() -> int
# cap.handle -> int (DLL handle)WGC Capture โ GPU Surface Texture
โ
โโโโโโโโโโโผโโโโโโโโโโโ
โ FrameArrived โ
โ if pausing โ โ โ โ Paused: return directly, zero D3D ops
โโโโโโโโโโโฌโโโโโโโโโโโ
โ
CopyResource (GPU async copy)
โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Double-buffered Staging โ
โ [0] Write โโ [1] Read โ
โ m_textureInUse anti-collide โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
Map (permanently mapped GPU memory)
โ
โโโโโโโ Zero-Copy Output โโโโโ
โ get_frame() โ
โ raw ptr โ numpy zero-copy โ
โ requires release_frame() โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโ One-Click Capture โโโโ
โ capture_one() โ
โ auto Pause/Resume โ
โ returns numpy array โ
โ zero GPU overhead when idle โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
User calls cap.pause()
โ
m_isPaused = true โโโโโ atomic flag, <1ฮผs
m_readableStagingIndex = -1
โ
โโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FrameArrived Callback (WGC still fires)โ
โ โ
โ lock(mutex); โ
โ if (m_isPaused) return; // โ pure CPU, skipโ
โ // โ below runs only after resume โ โ
โ CopyResource(staging, frame); โ
โ m_readableStagingIndex = idx; โ
โ unlock(mutex); โ
โโโโโโฒโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
User calls cap.resume() MapFrame checks readableStagingIndex
m_isPaused = false <0 โ no frame ready โ returns false
No WGC session destroy / no D3D device recreate / no callback re-register
โ zero-latency resume, no spikes
wgc_python/
โโโ wgc_python/ # Python package
โ โโโ __init__.py # Python API (ctypes FFI)
โ โโโ wgc_python.dll # Compiled DLL
โโโ wgc_python_dll/ # C++ DLL Project
โ โโโ WGCWindowCapture.h/cpp # Capture core (double-buffered + zero-copy)
โ โโโ WGCExport.h/cpp # DLL exports (thread-safe error handling)
โ โโโ D3DInterop.cpp # D3D11 device interop
โ โโโ WindowEnumerator.h/cpp # Window enumeration
โ โโโ pch.h # Precompiled header
โ โโโ packages/ # NuGet packages
โโโ test.py # Functional tests
โโโ test_mt.py # Multi-threaded on-demand capture example
โโโ pyproject.toml # pip build config
โโโ BUILD.md / BUILD_EN.md # Build instructions (CN/EN)
โโโ README.md / README_EN.md # Usage docs (CN/EN)
โโโ CONTRIBUTING.md # Contributing guide
โโโ CODE_OF_CONDUCT.md # Code of conduct
โโโ LICENSE # MIT License
โโโ requirements.txt # Python dependencies
See BUILD.md
| Issue | Solution |
|---|---|
| DLL not found | Ensure wgc_python.dll is in the correct location |
| Capture failed | Check if window is visible, Windows version >= 1903 |
| Non-ASCII path save failed | Use cv2.imencode + open().write() instead of cv2.imwrite |
| Missing dependencies | pip install numpy opencv-python |
- โ Game AI / Automation Scripts
- โ RPA Process Automation
- โ Screen Recording / Streaming
- โ UI Automation Testing
- โ Computer Vision Applications
This project is based on robmikh/Win32CaptureSample.
MIT License