Skip to content
Projects

XuanChenStar/wgc_python has 26 GitHub stars and 1 forks. GitHot measured 0 stars in 24 hours. Last checked 2026-11-15 14:02 UTC. Last push 2026-10-25.

How these counts are measured

Measured star growth

in 24h
0
in 7 days
Collecting history
in 15 days
Collecting history
in 1 month
Collecting history

Net star changes over overlapping periods, not daily totals. Missing measurements are left blank. Stars measure attention, not software quality.

About

๐Ÿš€ ไธ“ไธบ Python ่‡ชๅŠจๅŒ–ๆ‰“้€ ็š„้ซ˜ๆ€ง่ƒฝๆˆชๅ›พๅบ“ | 180+ FPS | ๆ”ฏๆŒๅŽๅฐ/้ฎๆŒก | ๅฏๆš‚ๅœ ้›ถๅŠŸ่€—ๅพ…ๆœบ;๐Ÿš€ High-performance capture library for Python automation | 180+ FPS | Background/Occluded Support | Pausable (Pause/Resume) Zero-Power Standby

Productivity

README_EN.md

Original

wgc_python

English | ็ฎ€ไฝ“ไธญๆ–‡

PyPI Python License

๐Ÿš€ Window Capture Library Built for Python Automation
High Frame Rate ยท Zero-Resource Standby ยท Capture Behind Windows ยท Minimalist API


Why wgc_python?

๐ŸŽฏ Designed for Automation Scenarios

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 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...

๐Ÿ“Š Performance Comparison

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 High โœ… High (continuous) High (session start/stop) High (can't truly pause)
wgc_python High โœ… Near Zero (when paused) Very Low (atomic flag) Zero (no D3D ops)

Qualitative comparison only; actual numbers vary by hardware, window content, and workload โ€” benchmark on your own setup.

โœจ Core Advantages

1. High Frame Rate

  • WGC captures GPU composition output directly instead of per-frame GDI screenshots โ€” frame rate ceiling far above PrintWindow-style approaches
  • Double-buffered Staging Texture: GPU async copy, read/write non-blocking
  • Zero-copy path: np.ndarray(strides=...) directly from GPU-mapped memory

2. Smart Resource Management

  • Pause/Resume 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

3. Minimalist API

  • 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

4. Multi-Instance Capture

  • 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

5. Client Area Precision (No Title Bar by Default)

  • 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

6. Cursor Capture Toggle

  • Default capture_cursor=True: frame includes the mouse cursor, same as typical screen recording
  • Set capture_cursor=False: frame excludes the mouse pointer, ideal for automation / data collection (also toggleable at runtime via set_cursor_capture_enabled())
  • Requires Windows 10 2004 (19041) or later; silently ignored on older systems

7. Capture Behind Windows

  • Supports capturing occluded, minimized, and background windows
  • Perfect for games, desktop apps, and various scenarios

Quick Start

Installation

Published on PyPI โ€” install directly with pip:

pip install wgc_python

Basic Usage

from 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()

# Disable mouse cursor capture (default capture_cursor=True, same as previous versions)
cap = WindowCapture("Notepad", "Notepad", capture_cursor=False)
frame = cap.capture_one()               # frame does not contain the mouse pointer
cap.set_cursor_capture_enabled(True)    # runtime toggle is also supported
cap.close()

Best Practice for Automation

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()

Monitor Capture (recommended for fullscreen game scenarios)

Captures the entire monitor where the target window resides instead of the window itself: the capture session is bound to the monitor, so the target window switching between fullscreen and windowed modes does NOT invalidate the capture session (window capture mode freezes on the last frame in this scenario).

from wgc_python import WindowCaptureMonitor

# Locate the monitor containing the target window and capture its full content (no client-area cropping)
cap = WindowCaptureMonitor("Game Window", "UnityWndClass")
frame = cap.capture_one()   # full-screen BGRA frame
cap.close()

Use cases: long-running automation over fullscreen games, scenarios where the target window's display mode changes frequently. Use WindowCapture instead when you need client-area cropping or to exclude other windows' content.

Zero-Copy Advanced Usage

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()

Real-time Display

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()

API Reference

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, capture_cursor=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.set_cursor_capture_enabled(enabled)  # Toggle cursor capture at runtime
#   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)

Technical Architecture

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 โ”‚
     โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

How Pause/Resume Works

  User calls cap.pause()
         โ”‚
    m_isPaused = true   โ—„โ”€โ”€โ”€โ”€ atomic flag, microsecond-level
    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

File Structure

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
โ”œโ”€โ”€ demon.py                      # Threaded realtime display 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

Build DLL

See BUILD.md


Requirements

  • Windows 10 1903+ (Build 18362); cursor capture toggle requires 2004+ (Build 19041)
  • Python 3.8+

Troubleshooting

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

Use Cases

  • โœ… Game AI / Automation Scripts
  • โœ… RPA Process Automation
  • โœ… Screen Recording / Streaming
  • โœ… UI Automation Testing
  • โœ… Computer Vision Applications

Acknowledgments

This project is based on robmikh/Win32CaptureSample.


License

MIT License