English | ็ฎไฝไธญๆ
๐ ไธบ Python ่ชๅจๅ่็็็ชๅฃๆ่ทๅบ
้ซๅธง็ๆ่ท ยท ้ถ่ตๆบๅพ ๆบ ยท ๆ ่ง้ฎๆก ยท 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 ๅฐ่ฃ | ้ซ | โ | ้ซ๏ผๆ็ปญ็ฉบ่ฝฌ๏ผ | ้ซ (ๅฏๅไผ่ฏๅผ้ๅคง) | ้ซ (ๆ ๆณ็ๆญฃๆๅ) |
| wgc_python | ้ซ | โ | ๆไฝ๏ผPauseๆถๅฝ้ถ๏ผ | ๆไฝ๏ผๅๅญๆ ๅฟไฝ๏ผ | ๅฝ้ถ๏ผๆ D3D ๆไฝ๏ผ |
่กจไธญไธบๅฎๆงๅฏนๆฏ๏ผๅ ทไฝๆฐๅผๅ ็กฌไปถใ็ชๅฃๅ ๅฎนไธๅบๆฏ่ๅผ๏ผๅปบ่ฎฎไปฅ่ชๅทฑ็ๅฎๆตไธบๅใ
- WGC ็ดๆฅๆ่ท GPU ๅๆ่พๅบ๏ผไธ้ๅธงๆชๅฑ๏ผๅธง็ไธ้่ฟ้ซไบ PrintWindow ็ญ GDI ๆนๆก
- ๅ็ผๅฒ Staging ็บน็๏ผGPU ๅผๆญฅๆท่ด๏ผ่ฏปๅไบไธ้ปๅก
- ้ถๆท่ดๅๅฅฝ๏ผ
np.ndarray(strides=...)็ดๆฅไป GPU ๆ ๅฐๅ ๅญๆ้ ่งๅพ
- Pause/Resume ่ฝฏๆๅ๏ผไธ้ๆฏไธ้ๅปบ 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
- ้ป่ฎค
capture_cursor=True๏ผ็ป้ขๅ ๅซ้ผ ๆ ๅ ๆ ๏ผไธๅธธ่งๅฝๅฑ่กไธบไธ่ด - ่ฎพ็ฝฎ
capture_cursor=False๏ผ็ป้ขไธๅซ้ผ ๆ ๆ้๏ผ้ๅ่ชๅจๅ / ๆฐๆฎ้้ๅบๆฏ๏ผไนๅฏ็จset_cursor_capture_enabled()่ฟ่กๆถๅๆข๏ผ - ้ Windows 10 2004 (19041) ๅไปฅไธ็ณป็ป๏ผๆง็ณป็ป่ชๅจๅฟฝ็ฅ่ฏฅ้้กน
- ๆฏๆๆ่ท่ขซ้ฎๆกใๆๅฐๅใๅๅฐ็ชๅฃ
- ๅฎ็พ้้ ๆธธๆใๆก้ขๅบ็จ็ญๅ็งๅบๆฏ
ๅทฒๅๅธ่ณ PyPI๏ผ็ดๆฅ pip ๅฎ่ฃ ๅณๅฏ๏ผ
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()
# ไธๆ่ท้ผ ๆ ๅ
ๆ ๏ผ้ป่ฎค capture_cursor=True๏ผไฟๆๆง็่กไธบ๏ผ
cap = WindowCapture("่ฎฐไบๆฌ", "Notepad", capture_cursor=False)
frame = cap.capture_one() # ็ป้ขไธๅซ้ผ ๆ ๆ้
cap.set_cursor_capture_enabled(True) # ไนๆฏๆ่ฟ่กๆถๅๆข
cap.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 WindowCaptureMonitor
# ๅฎไฝ็ฎๆ ็ชๅฃๆๅจ็ๆพ็คบๅจๅนถๆ่ทๅ
ถๅ
จ้จๅ
ๅฎน๏ผๆ ๅฎขๆทๅบ่ฃๅช๏ผ
cap = WindowCaptureMonitor("ๆธธๆ็ชๅฃ", "UnityWndClass")
frame = cap.capture_one() # ๆดๅฑ BGRA ๅธง
cap.close()้็จๅบๆฏ๏ผๅ
จๅฑๆธธๆ็้ฟๆ่ชๅจๅ้้ใ็ฎๆ ็ชๅฃๆพ็คบๆจกๅผ้ข็นๅๅ็ๅบๆฏใ้่ฆ่ฃๅชๅฎขๆทๅบๆๆ้คๅ
ถไป็ชๅฃๅ
ๅฎนๆถ๏ผไป่ฏทไฝฟ็จ WindowCaptureใ
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, capture_cursor=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.set_cursor_capture_enabled(enabled) # ่ฟ่กๆถๅๆขๅ
ๆ ๆ่ท
# 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 โโโโโ ๅๅญๆ ๅฟไฝ๏ผๅพฎ็ง็บง
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 # ๅ่ฝๆต่ฏ
โโโ demon.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)๏ผๅ ๆ ๆ่ทๅผๅ ณ้ 2004+ (Build 19041)
- Python 3.8+
่ฏฆ่ง 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
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...| 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.
- 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
- 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
- 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
- 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 viaset_cursor_capture_enabled()) - Requires Windows 10 2004 (19041) or later; silently ignored on older systems
- Supports capturing occluded, minimized, and background windows
- Perfect for games, desktop apps, and various scenarios
Published on PyPI โ install directly with pip:
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()
# 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()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, 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)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, 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
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
See BUILD.md
- Windows 10 1903+ (Build 18362); cursor capture toggle requires 2004+ (Build 19041)
- Python 3.8+
| 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