DXcam

repository·main·Indexed 21 days ago

https://github.com/ra1nty/dxcam

A high-performance Python screenshot library for Windows (v0.3.0) utilizing the Desktop Duplication API (DXGI) and Windows Graphics Capture (WinRT). Optimized for low-latency, high-FPS pipelines for AI agents and computer vision, it supports both single-shot grabs and continuous threaded capture into a ring buffer. Features include multiple output color modes (RGB, RGBA, BGR, BGRA, GRAY), configurable capture and processor backends (cv2, numpy), and support for region-specific captures.

Tokens
3.6K
Snippets
24
Records
25
Agent score
74%

What's inside dxcam

  1. Configure capture and processor backends

    main

    DXcam uses a two-stage pipeline: a Capture Backend to acquire the frame and a Processor Backend to handle color conversion and cropping.

    Capture Backends

    • dxgi (default): Desktop Duplication API. Best for most workloads and one-shot grabs.
    • winrt: Windows Graphics Capture. Use this if you need to render the mouse cursor.

    Processor Backends

    • cv2 (default): Uses OpenCV for color conversion. Recommended if OpenCV is installed.
    • numpy: Uses compiled Cython kernels. Best for lean installations without OpenCV.

    Example Configuration:

    # Using WinRT and NumPy
    camera = dxcam.create(backend="winrt", processor_backend="numpy")
  2. Perform continuous screen capture

    main

    For high-FPS video capture or machine learning workloads, use start() to spin up a background thread that polls frames into an in-memory ring buffer.

    1. Start capture: camera.start(region=(...), target_fps=...).
    2. Consume frames: Use camera.get_latest_frame() to retrieve frames from the buffer. This method blocks until a frame is available.
    3. Stop capture: camera.stop().

    Example:

    camera.start(region=(left, top, right, bottom), target_fps=60)
    # ... consume frames ...
    for _ in range(1000):
        frame = camera.get_latest_frame()
    camera.stop()
    camera.start(region=(left, top, right, bottom), target_fps=60)
    # ...
    frame = camera.get_latest_frame()
    camera.stop()
  3. Safely release DXcam resources

    main

    To free buffers and release capture resources, call camera.release(). Once released, the instance cannot be reused and calling start() will raise a RuntimeError.

    It is recommended to use a context manager to ensure resources are released automatically.

    Manual release:

    camera = dxcam.create()
    camera.release()

    Context manager (Recommended):

    with dxcam.create() as camera:
        frame = camera.grab()
    # resources released automatically here
    with dxcam.create() as camera:
        frame = camera.grab()
  4. Install DXcam

    main

    You can install DXcam via pip. Choose between a minimal installation or a full feature installation that includes OpenCV-based color conversion and WinRT capture backend support.

    Minimal install:

    pip install dxcam

    Full feature install (recommended):

    pip install "dxcam[cv2,winrt]"

    Note: Official Windows wheels support CPython 3.10 to 3.14.

    pip install "dxcam[cv2,winrt]"
  5. Perform high-speed threaded capture

    main

    For continuous video capture, use start() to launch a background thread that fills an internal ring buffer. This allows you to retrieve the latest frames without the overhead of direct DXGI calls in your main loop.

    Arguments:

    • region (Region, optional): The capture area. If provided, it updates the camera's region.
    • target_fps (int): Target capture FPS. Setting to 0 disables timer pacing.
    • video_mode (bool): If True, the capture loop reuses the previous frame when no new frame arrives.
    • delay (int): Optional startup delay in seconds.

    Workflow:

    1. Call cam.start(...).
    2. Retrieve frames using cam.get_latest_frame() or cam.get_latest_frame_view().
    3. Call cam.stop() to end capture.
    import dxcam
    
    with dxcam.create(output_color="BGR", processor_backend="cv2") as cam:
        # Start threaded capture at 60 FPS
        cam.start(target_fps=60, video_mode=True)
        
        # Retrieve the latest frame
        frame, ts = cam.get_latest_frame(with_timestamp=True)
        
        cam.stop()
  6. Use video_mode for consistent FPS

    main

    By default, DXcam only captures newly rendered frames. If you enable video_mode=True in camera.start(), DXcam will fill the buffer at the target_fps by reusing the previous frame if no new frame is rendered. This is ideal for video recording with a constant frame rate.

    Example:

    import cv2
    import dxcam
    
    target_fps = 30
    camera = dxcam.create(output_color="BGR")
    camera.start(target_fps=target_fps, video_mode=True)
    
    writer = cv2.VideoWriter("video.mp4", cv2.VideoWriter_fourcc(*"mp4v"), target_fps, (1920, 1080))
    for _ in range(600):
        writer.write(camera.get_latest_frame())
    
    camera.stop()
    writer.release()
    camera.start(target_fps=target_fps, video_mode=True)
  7. Take a single screenshot with grab()

    main

    The grab() method returns a numpy.ndarray of the current screen.

    • Default behavior: Returns None if no new frame is available since the last capture. To force the latest frame, use camera.grab(new_frame_only=False).
    • Region capture: Pass a region tuple (left, top, right, bottom) to capture a specific area.
    • Zero-copy: Use copy=False or camera.grab_view() for a faster, zero-copy view. Note that the returned buffer can be overwritten by subsequent captures.

    Example:

    # Capture a 640x640 region
    left, top = (1920 - 640) // 2, (1080 - 640) // 2
    right, bottom = left + 640, top + 640
    frame = camera.grab(region=(left, top, right, bottom))
    frame = camera.grab(region=(left, top, right, bottom))
  8. Create a DXcam instance

    main

    Use dxcam.create() to initialize a camera instance. Each instance is associated with one monitor (output). You can specify the device index, output index, color mode, and backends during creation.

    Basic usage:

    import dxcam
    camera = dxcam.create()  # primary output on device 0

    Custom configuration:

    camera = dxcam.create(
        device_idx=0, 
        output_idx=0, 
        output_color="BGR",
        backend="dxgi",
        processor_backend="cv2"
    )
    import dxcam
    camera = dxcam.create()
  9. Inspect available devices and outputs

    main

    Use dxcam.device_info() and dxcam.output_info() to discover available GPUs and monitors on your system.

    Example:

    import dxcam
    print(dxcam.device_info())
    print(dxcam.output_info())
    print(dxcam.device_info())
    print(dxcam.output_info())
  10. Retrieve frame timestamps

    main

    When running in capture mode, you can retrieve the timestamp of the most recent frame (in seconds) using get_latest_frame(with_timestamp=True). This is useful for synchronizing video or ML workloads.

    Example:

    camera.start(target_fps=60)
    frame, ts = camera.get_latest_frame(with_timestamp=True)
    camera.stop()
    frame, ts = camera.get_latest_frame(with_timestamp=True)
  11. Configure output color modes

    main

    You can specify the output color format when creating the camera using the output_color parameter. The data is returned as a numpy.ndarray.

    Supported modes:

    • "RGB" (Requires cv2 or numpy)
    • "RGBA" (Requires cv2 or numpy)
    • "BGR" (Requires cv2 or numpy)
    • "BGRA" (Leanest path; does not require OpenCV)
    • "GRAY" (Requires cv2 or numpy)

    Example:

    camera = dxcam.create(output_color="BGRA")
    camera = dxcam.create(output_color="BGRA")
  12. Use threaded capture with start() and get_latest_frame()

    main

    For high-performance or continuous capture, use the threaded mode by calling cam.start(target_fps=...). This allows you to retrieve the most recent frame without waiting for a new capture cycle to complete.

    import dxcam
    
    cam = dxcam.create(backend="dxgi")
    cam.start(target_fps=60)
    
    # Retrieve the most recent frame from the buffer
    frame = cam.get_latest_frame()
    
    cam.stop()
    cam.release()