Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mod #1

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
161 changes: 22 additions & 139 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,152 +1,35 @@
# 📸 **BetterCam** 🚀
![World's Best AI Aimbot Banner](images/banner.png)
RTX 4070 + RYZEN 7 5800X 48GB RAM

[![Pull Requests Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat)](http://makeapullrequest.com)
> ***🌟 World's Fastest Python Screenshot Library for Windows 🐍***
SCREEN CAPTURE RESOLUTION AT 1920X1080

```python
import bettercam
camera = bettercam.create()
camera.grab()
```
TEST FROM : BetterCam/benchmarks/bettercam_capture.py

## 🌈 Introduction
BetterCam is the World's 🌏 Fastest Publicly available Python screenshot library for Windows, boasting 240Hz+ capturing using the Desktop Duplication API 🖥️💨. Born from [DXCam](https://github.com/ra1nty/DXcam), it shines in deep learning pipelines for FPS games, outpacing other Python solutions like [python-mss](https://github.com/BoboTiG/python-mss) and [D3DShot](https://github.com/SerpentAI/D3DShot/).

BetterCam's superpowers include:
- 🚅 Insanely fast screen capturing (> 240Hz)
- 🎮 Capture from Direct3D exclusive full-screen apps without interruption, even during alt+tab.
- 🔧 Auto-adjusts to scaled / stretched resolutions.
- 🎯 Precise FPS targeting for Video output.
- 👌 Smooth NumPy, OpenCV, PyTorch integration, etc.
Screen Capture FPS: 640
[BetterCam] Capture benchmark with NVIDIA GPU
Elapsed time: 3.53 seconds
Frames per second: 283.38 FPS

> ***💞 Community contributions warmly invited!***

## 🛠️ Installation
### From PyPI:
```bash
pip install bettercam
```
Screen Capture FPS: 622
[BetterCam] Capture benchmark with Torch CUDA
Elapsed time: 3.36 seconds
Frames per second: 297.91 FPS

**Note:** 🧩 OpenCV is needed by BetterCam for color space conversion. Install it with `pip install opencv-python` if not yet available.

Screen Capture FPS: 657
[BetterCam] Capture benchmark without GPU acceleration
Elapsed time: 3.50 seconds
Frames per second: 286.06 FPS

## 📚 Usage
Each monitor is paired with a `BetterCam` instance.
To get started:
```python
import bettercam
camera = bettercam.create() # Primary monitor's BetterCam instance
```
### 📷 Screenshot
For a quick snap, call `.grab`:
```python
frame = camera.grab()
```
`frame` is a `numpy.ndarray` in the `(Height, Width, 3[RGB])` format by default. Note: `.grab` may return `None` if there's no update since the last `.grab`.
Comparison Results:
NVIDIA GPU - Elapsed time: 3.53 seconds, FPS: 283.38
Torch CUDA - Elapsed time: 3.36 seconds, FPS: 297.91
No GPU - Elapsed time: 3.50 seconds, FPS: 286.06

To display your screenshot:
```python
from PIL import Image
Image.fromarray(frame).show()
```
For a specific region, provide the `region` parameter with a tuple for the bounding box coordinates:
```python
left, top = (1920 - 640) // 2, (1080 - 640) // 2
right, bottom = left + 640, top + 640
region = (left, top, right, bottom)
frame = camera.grab(region=region) # A 640x640x3 numpy ndarray snapshot
```
camera = bettercam.create(output_idx=0, output_color="BGRA", nvidia_gpu=True) TO USE DIRECTLY CUPY_PROCESSOR

### 📹 Screen Capture
Start and stop screen capture with `.start` and `.stop`:
```python
camera.start(region=(left, top, right, bottom)) # Capture a region (optional)
camera.is_capturing # True
# ... Your Code
camera.stop()
camera.is_capturing # False
```
camera = bettercam.create(output_idx=0, output_color="BGRA", torch_cuda=True) TO USE DIRECTLY TORCH_CUDA_PROCESSOR

### 🔄 Retrieving Captured Data
When capturing, grab the latest frame with `.get_latest_frame`:
```python
camera.start()
for i in range(1000):
image = camera.get_latest_frame() # Waits for a new frame
camera.stop()
```
camera = bettercam.create(output_idx=0, output_color="BGRA") TO USE DIRECTLY NUMPY_PROCESSOR

## ⚙️ Advanced Usage & Notes
### 🖥️ Multiple Monitors / GPUs
```python
cam1, cam2, cam3 = [bettercam.create(device_idx=d, output_idx=o) for d, o in [(0, 0), (0, 1), (1, 1)]]
img1, img2, img3 = [cam.grab() for cam in (cam1, cam2, cam3)]
```
To list devices and outputs:
```pycon
>>> import bettercam
>>> bettercam.device_info()
>>> bettercam.output_info()
```

### 🎨 Output Format
Select your color mode when creating a BetterCam instance:
```python
bettercam.create(output_idx=0, output_color="BGRA")
```
We support "RGB", "RGBA", "BGR", "BGRA", "GRAY" (for grayscale). Right now only `numpy.ndarray` shapes are supported: `(Height, Width, Channels)`.

### 🔄 Video Buffer
Frames go into a fixed-size ring buffer. Customize its max length with `max_buffer_len` on creation:
```python
camera = bettercam.create(max_buffer_len=512)
```

### 🎥 Target FPS
For precise FPS targeting, we use the high-resolution `CREATE_WAITABLE_TIMER_HIGH_RESOLUTION`:
```python
camera.start(target_fps=120) # Ideally, not beyond 240Hz.
```

### 🔄 Video Mode
For constant framerate video recording, use `video_mode=True` during `.start`:
```python
# Example: Record a 5-second, 120Hz video
camera.start(target_fps=target_fps, video_mode=True)
# ... Video writing code goes here
```

### 🛠️ Resource Management
Call `.release` to stop captures and free resources. Manual deletion also possible:
```python
del camera
```

## 📊 Benchmarks
### Max FPS Achievement:
```python
cam = bettercam.create()
# ... Benchmarking code...
```
| | BetterCam Nvidia GPU :checkered_flag: | BetterCam :checkered_flag: | DXCam | python-mss | D3DShot |
|---------|---------------------------------------|--------------------------|--------|------------|---------|
| Avg FPS | 111.667 | 123.667 | 39 | 34.667 | N/A |
| Std Dev | 0.889 | 1.778 | 1.333 | 2.222 | N/A |

### FPS Targeting:
```python
# ... Sample code to test target FPS ...
```
| Target/Result | BetterCam Nvidia GPU :checkered_flag: | BetterCam :checkered_flag: | DXCam | python-mss | D3DShot |
|---------------|---------------------------------------|--------------------------|-------|------------|---------|
| 120fps | 111.667, 0.889 | 88.333, 2.444 | 36.667, 0.889 | N/A | N/A |
| 60fps | 60, 0 | 60, 0 | 35, 5.3 | N/A | N/A |

## 📝 Referenced Work
- [DXCam](https://github.com/ra1nty/DXcam): Our origin story.
- [D3DShot](https://github.com/SerpentAI/D3DShot/): Provided foundational ctypes.
- [OBS Studio](https://github.com/obsproject/obs-studio): A treasure trove of knowledge.

[^1]: [Preemption (computing)](https://en.wikipedia.org/wiki/Preemption_(computing))
[^2]: [Time.sleep precision improvement](https://github.com/python/cpython/issues/65501)
113 changes: 99 additions & 14 deletions benchmarks/bettercam_capture.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,103 @@
import time
import bettercam

def benchmark_nvidia_gpu():
TOP = 0
LEFT = 0
RIGHT = 1920
BOTTOM = 1080
region = (LEFT, TOP, RIGHT, BOTTOM)
title = "[BetterCam] Capture benchmark with NVIDIA GPU"

camera = bettercam.create(output_idx=0, output_color="BGRA", nvidia_gpu=True)
camera.start(target_fps=0, video_mode=True)

start_time = time.time()

for i in range(1000):
image = camera.get_latest_frame()

end_time = time.time()
elapsed_time = end_time - start_time
fps = 1000 / elapsed_time

camera.stop()
del camera

print(f"{title}")
print(f"Elapsed time: {elapsed_time:.2f} seconds")
print(f"Frames per second: {fps:.2f} FPS")
return elapsed_time, fps

def benchmark_torch_cuda():
TOP = 0
LEFT = 0
RIGHT = 1920
BOTTOM = 1080
region = (LEFT, TOP, RIGHT, BOTTOM)
title = "[BetterCam] Capture benchmark with Torch CUDA"

camera = bettercam.create(output_idx=0, output_color="BGRA", torch_cuda=True)
camera.start(target_fps=0, video_mode=True)

start_time = time.time()

for i in range(1000):
image = camera.get_latest_frame()

end_time = time.time()
elapsed_time = end_time - start_time
fps = 1000 / elapsed_time

camera.stop()
del camera

print(f"{title}")
print(f"Elapsed time: {elapsed_time:.2f} seconds")
print(f"Frames per second: {fps:.2f} FPS")
return elapsed_time, fps

def benchmark_no_gpu():
TOP = 0
LEFT = 0
RIGHT = 1920
BOTTOM = 1080
region = (LEFT, TOP, RIGHT, BOTTOM)
title = "[BetterCam] Capture benchmark without GPU acceleration"

camera = bettercam.create(output_idx=0, output_color="BGRA")
camera.start(target_fps=0, video_mode=True)

start_time = time.time()

for i in range(1000):
image = camera.get_latest_frame()

end_time = time.time()
elapsed_time = end_time - start_time
fps = 1000 / elapsed_time

camera.stop()
del camera

print(f"{title}")
print(f"Elapsed time: {elapsed_time:.2f} seconds")
print(f"Frames per second: {fps:.2f} FPS")
return elapsed_time, fps

# Benchmark with NVIDIA GPU
nvidia_time, nvidia_fps = benchmark_nvidia_gpu()

# Benchmark with Torch CUDA
torch_time, torch_fps = benchmark_torch_cuda()

# Benchmark without GPU acceleration
no_gpu_time, no_gpu_fps = benchmark_no_gpu()

# Print comparison results
print("\nComparison Results:")
print(f"NVIDIA GPU - Elapsed time: {nvidia_time:.2f} seconds, FPS: {nvidia_fps:.2f}")
print(f"Torch CUDA - Elapsed time: {torch_time:.2f} seconds, FPS: {torch_fps:.2f}")
print(f"No GPU - Elapsed time: {no_gpu_time:.2f} seconds, FPS: {no_gpu_fps:.2f}")


TOP = 0
LEFT = 0
RIGHT = 1920
BOTTOM = 1080
region = (LEFT, TOP, RIGHT, BOTTOM)
title = "[BetterCam] Capture benchmark"

fps = 0
camera = bettercam.create(output_idx=0, output_color="BGRA")
camera.start(target_fps=60, video_mode=True)
for i in range(1000):
image = camera.get_latest_frame()
camera.stop()
del camera
9 changes: 7 additions & 2 deletions bettercam/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ def create(
device_idx: int = 0,
output_idx: int = None,
region: tuple = None,
output_color: str = "RGB",
output_color: str = "BGRA",
nvidia_gpu: bool = False,
torch_cuda: bool = False,
max_buffer_len: int = 64,
):
device = self.devices[device_idx]
Expand Down Expand Up @@ -75,6 +76,7 @@ def create(
region=region,
output_color=output_color,
nvidia_gpu=nvidia_gpu,
torch_cuda=torch_cuda,
max_buffer_len=max_buffer_len,
)
self._camera_instances[instance_key] = camera
Expand Down Expand Up @@ -108,8 +110,9 @@ def create(
device_idx: int = 0,
output_idx: int = None,
region: tuple = None,
output_color: str = "RGB",
output_color: str = "BGRA",
nvidia_gpu: bool = False,
torch_cuda: bool = False,
max_buffer_len: int = 64,
):
return __factory.create(
Expand All @@ -118,6 +121,7 @@ def create(
region=region,
output_color=output_color,
nvidia_gpu=nvidia_gpu,
torch_cuda=torch_cuda,
max_buffer_len=max_buffer_len,
)

Expand All @@ -128,3 +132,4 @@ def device_info():

def output_info():
return __factory.output_info()

Loading