Skip to content

Commit 48fbecb

Browse files
authored
Add files via upload
Signed-off-by: Bubbles The Dev <[email protected]>
1 parent 69288c6 commit 48fbecb

File tree

5 files changed

+334
-0
lines changed

5 files changed

+334
-0
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
@echo off
2+
REM Directly set the file path to config.py
3+
set scriptPath="config.py"
4+
5+
REM Verify the entered file path exists
6+
if not exist %scriptPath% (
7+
echo Error: The specified file does not exist. Please check the path.
8+
pause >nul
9+
exit /b 1
10+
)
11+
12+
REM Open the config.py file in Notepad
13+
echo Opening config.py in Notepad...
14+
notepad %scriptPath%
15+
16+
REM Keep the window open
17+
echo Press any key to exit...
18+
pause >nul

Run-Application/config.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# Configuration for BetterCam Screen Capture and YOLO model
2+
3+
# 🔍 Screen Capture Settings
4+
screenWidth = 640 # Width of the screen region to capture
5+
screenHeight = 640 # Height of the screen region to capture
6+
7+
# 🎯 Object Detection Settings
8+
confidenceThreshold = 0.5 # Minimum confidence to draw detection
9+
nmsThreshold = 0.4 # Non-max suppression threshold
10+
11+
# 📦 YOLO Model Selection
12+
# Options: 'torch' (YOLOv5/v8 .pt), 'onnx' (.onnx), or 'engine' (.engine for TensorRT)
13+
modelType = 'torch'
14+
15+
# 🧠 Model File Paths
16+
torchModelPath = 'models/yolov8n.pt' # PyTorch model (YOLOv5/YOLOv8)
17+
onnxModelPath = 'models/yolov8n.onnx' # ONNX model path
18+
tensorrtModelPath = 'models/yolov8n.engine' # TensorRT engine file
19+
20+
# 🎥 BetterCam Capture Settings
21+
targetFPS = 60 # Target frames per second
22+
maxBufferLen = 512 # Max buffer size for frame storage
23+
region = None # Set to None for full-screen capture
24+
monitorIdx = 0 # Use 0 for primary monitor
25+
26+
# 🖍️ Drawing Bounding Boxes
27+
boundingBoxColor = (0, 255, 0) # BGR Green
28+
highlightColor = (0, 0, 255) # BGR Red for highlights
29+
30+
# 🪟 Overlay Settings
31+
overlayWidth = 1920
32+
overlayHeight = 1080
33+
overlayAlpha = 200 # 0 (fully transparent) to 255 (fully opaque)
34+
35+
# ⚡ GPU Backend Preferences
36+
useCuda = True # Enable CUDA (for NVIDIA GPUs)
37+
useDirectML = True # Enable DirectML (for AMD GPUs or fallback)

Run-Application/launcher.bat

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
@echo off
2+
REM Save the current directory
3+
pushd %~dp0
4+
5+
REM Check if main_cpu.py exists
6+
if not exist main_cpu.py (
7+
echo Error: main_cpu.py not found in the current directory.
8+
popd
9+
pause
10+
exit /b 1
11+
)
12+
13+
REM Run the Python script and check for errors
14+
echo Running main_cpu.py...
15+
python main_cpu.py
16+
if %errorlevel% neq 0 (
17+
echo Error: main_cpu.py did not run successfully. Error level: %errorlevel%
18+
popd
19+
pause
20+
exit /b %errorlevel%
21+
)
22+
23+
REM Provide success feedback
24+
echo main_cpu.py ran successfully.
25+
26+
REM Return to the original directory
27+
popd
28+
pause

Run-Application/main.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
import sys
2+
import os
3+
import time
4+
import numpy as np
5+
import cupy as cp
6+
import cv2
7+
import torch
8+
import torchvision
9+
import torchaudio
10+
import torch_directml
11+
import onnx
12+
import onnxsim
13+
import onnxruntime as ort
14+
import onnxruntime_directml
15+
import bettercam
16+
import config
17+
import customtkinter as ctk
18+
import win32api
19+
import win32con
20+
import win32gui
21+
from ultralytics import YOLO
22+
from colorama import Fore, Style, init
23+
from overlay import Overlay
24+
import tensorrt as trt
25+
26+
# ------------------ TensorRT Inference Class ------------------ #
27+
class TensorRTInference:
28+
def __init__(self, engine_path):
29+
self.logger = trt.Logger(trt.Logger.WARNING)
30+
self.runtime = trt.Runtime(self.logger)
31+
with open(engine_path, 'rb') as f:
32+
self.engine = self.runtime.deserialize_cuda_engine(f.read())
33+
self.context = self.engine.create_execution_context()
34+
self.input_shape = self.engine.get_binding_shape(0)
35+
self.output_shape = self.engine.get_binding_shape(1)
36+
self.input_size = np.prod(self.input_shape) * np.float32().nbytes
37+
self.output_size = np.prod(self.output_shape) * np.float32().nbytes
38+
self.d_input = cp.cuda.alloc(self.input_size)
39+
self.d_output = cp.cuda.alloc(self.output_size)
40+
self.bindings = [int(self.d_input.ptr), int(self.d_output.ptr)]
41+
42+
def infer(self, input_data: np.ndarray) -> np.ndarray:
43+
input_data = input_data.astype(np.float32).ravel()
44+
output_data = np.empty(self.output_shape, dtype=np.float32)
45+
cp.cuda.runtime.memcpy(self.d_input.ptr, input_data.ctypes.data, self.input_size, cp.cuda.runtime.memcpyHostToDevice)
46+
self.context.execute_v2(self.bindings)
47+
cp.cuda.runtime.memcpy(output_data.ctypes.data, self.d_output.ptr, self.output_size, cp.cuda.runtime.memcpyDeviceToHost)
48+
return output_data.reshape(self.output_shape)
49+
50+
# ------------------ BetterCam Enhanced ------------------ #
51+
class BetterCamEnhanced:
52+
def __init__(self, max_buffer_len=config.maxBufferLen, target_fps=config.targetFPS, region=None, monitor_idx=0):
53+
self.camera = None
54+
self.max_buffer_len = max_buffer_len
55+
self.target_fps = target_fps
56+
self.region = region
57+
self.monitor_idx = monitor_idx
58+
self.is_capturing = False
59+
60+
def start(self):
61+
self.camera = bettercam.create(monitor_idx=self.monitor_idx, max_buffer_len=self.max_buffer_len)
62+
self.camera.start(target_fps=self.target_fps)
63+
self.is_capturing = True
64+
65+
def grab_frame(self):
66+
return self.camera.grab(region=self.region) if self.region else self.camera.grab()
67+
68+
def stop(self):
69+
self.camera.stop()
70+
self.is_capturing = False
71+
72+
# ------------------ Load Model ------------------ #
73+
def load_model():
74+
model_type = config.modelType.lower()
75+
76+
if model_type == 'torch':
77+
print(Fore.CYAN + "[INFO] Loading PyTorch model using Ultralytics...")
78+
return YOLO(config.torchModelPath), 'torch'
79+
80+
elif model_type == 'onnx':
81+
available_providers = ort.get_available_providers()
82+
83+
if 'CUDAExecutionProvider' in available_providers:
84+
providers = ['CUDAExecutionProvider']
85+
print(Fore.GREEN + "[INFO] ONNX model will use CUDAExecutionProvider (NVIDIA GPU).")
86+
elif 'DmlExecutionProvider' in available_providers or 'DirectMLExecutionProvider' in available_providers:
87+
providers = ['DmlExecutionProvider'] if 'DmlExecutionProvider' in available_providers else ['DirectMLExecutionProvider']
88+
print(Fore.YELLOW + "[INFO] ONNX model will use DirectMLExecutionProvider (AMD GPU).")
89+
else:
90+
providers = ['CPUExecutionProvider']
91+
print(Fore.RED + "[INFO] ONNX model will use CPUExecutionProvider (CPU only).")
92+
93+
session = ort.InferenceSession(config.onnxModelPath, providers=providers)
94+
return session, 'onnx'
95+
96+
elif model_type == 'engine':
97+
print(Fore.MAGENTA + "[INFO] Loading TensorRT engine with CuPy...")
98+
return TensorRTInference(config.tensorrtModelPath), 'engine'
99+
100+
else:
101+
raise ValueError(Fore.RED + "Unsupported modelType in config.py. Use 'torch', 'onnx', or 'engine'.")
102+
103+
# ------------------ Object Detection ------------------ #
104+
def detect_objects(model, model_type, frame):
105+
if model_type == 'torch':
106+
return model.predict(source=frame, imgsz=(config.screenWidth, config.screenHeight), conf=config.confidenceThreshold, verbose=False)
107+
elif model_type == 'onnx':
108+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
109+
resized = cv2.resize(frame_rgb, (config.screenWidth, config.screenHeight)).astype(np.float32)
110+
tensor = resized.transpose(2, 0, 1)[np.newaxis] / 255.0
111+
return model.run(None, {model.get_inputs()[0].name: tensor})
112+
elif model_type == 'engine':
113+
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
114+
resized = cv2.resize(frame_rgb, (config.screenWidth, config.screenHeight)).astype(np.float32)
115+
tensor = resized.transpose(2, 0, 1)[np.newaxis] / 255.0
116+
return model.infer(tensor)
117+
return None
118+
119+
# ------------------ Draw Bounding Boxes ------------------ #
120+
def draw_boxes(frame, results, model_type):
121+
if results is None:
122+
return frame
123+
if model_type == 'torch':
124+
for r in results:
125+
for box in r.boxes.xyxy.cpu().numpy().astype(int):
126+
x1, y1, x2, y2 = box[:4]
127+
cv2.rectangle(frame, (x1, y1), (x2, y2), config.boundingBoxColor, 2)
128+
elif model_type in ['onnx', 'engine']:
129+
for det in results[0]:
130+
if det[4] > config.confidenceThreshold:
131+
x1, y1, x2, y2 = map(int, det[:4])
132+
cv2.rectangle(frame, (x1, y1), (x2, y2), config.boundingBoxColor, 2)
133+
return frame
134+
135+
def extract_boxes(results, model_type):
136+
boxes = []
137+
if model_type == 'torch':
138+
for r in results:
139+
for box in r.boxes.xyxy.cpu().numpy().astype(int):
140+
x1, y1, x2, y2 = box[:4]
141+
boxes.append([x1, y1, x2, y2])
142+
elif model_type in ['onnx', 'engine']:
143+
for det in results[0]:
144+
if det[4] > config.confidenceThreshold:
145+
x1, y1, x2, y2 = map(int, det[:4])
146+
boxes.append([x1, y1, x2, y2])
147+
return boxes
148+
149+
# ------------------ Main ------------------ #
150+
def main():
151+
init(autoreset=True)
152+
input("Start your game and press Enter to continue...")
153+
154+
camera = BetterCamEnhanced(target_fps=config.targetFPS, monitor_idx=config.monitorIdx)
155+
camera.start()
156+
157+
model, model_type = load_model()
158+
overlay = Overlay(width=config.overlayWidth, height=config.overlayHeight, alpha=config.overlayAlpha)
159+
overlay.toggle()
160+
161+
try:
162+
while True:
163+
frame = camera.grab_frame()
164+
if frame is None:
165+
continue
166+
167+
results = detect_objects(model, model_type, frame)
168+
frame = draw_boxes(frame, results, model_type)
169+
boxes = extract_boxes(results, model_type)
170+
overlay.update(boxes)
171+
172+
cv2.imshow("YOLO Detection", frame)
173+
if cv2.waitKey(1) & 0xFF == ord('q'):
174+
break
175+
except KeyboardInterrupt:
176+
pass
177+
finally:
178+
camera.stop()
179+
overlay.toggle()
180+
cv2.destroyAllWindows()
181+
182+
if __name__ == "__main__":
183+
main()

Run-Application/overlay.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import win32gui
2+
import win32con
3+
import win32api
4+
5+
class Overlay:
6+
def __init__(self, width=1920, height=1080, alpha=200):
7+
self.hInstance = win32api.GetModuleHandle()
8+
self.className = "OverlayWindowClass"
9+
self.hWnd = None
10+
self.width = width
11+
self.height = height
12+
self.alpha = alpha
13+
self.visible = False
14+
15+
self._register_class()
16+
self._create_window()
17+
18+
def _register_class(self):
19+
wndClass = win32gui.WNDCLASS()
20+
wndClass.lpfnWndProc = self._wnd_proc
21+
wndClass.hInstance = self.hInstance
22+
wndClass.lpszClassName = self.className
23+
wndClass.hCursor = win32gui.LoadCursor(None, win32con.IDC_ARROW)
24+
wndClass.hbrBackground = win32con.COLOR_WINDOW
25+
win32gui.RegisterClass(wndClass)
26+
27+
def _create_window(self):
28+
screen_w = win32api.GetSystemMetrics(0)
29+
screen_h = win32api.GetSystemMetrics(1)
30+
31+
self.hWnd = win32gui.CreateWindowEx(
32+
win32con.WS_EX_LAYERED | win32con.WS_EX_TOPMOST | win32con.WS_EX_TOOLWINDOW | win32con.WS_EX_TRANSPARENT,
33+
self.className,
34+
None,
35+
win32con.WS_POPUP,
36+
0,
37+
0,
38+
screen_w,
39+
screen_h,
40+
None,
41+
None,
42+
self.hInstance,
43+
None
44+
)
45+
46+
win32gui.SetLayeredWindowAttributes(self.hWnd, 0x000000, self.alpha, win32con.LWA_ALPHA)
47+
win32gui.ShowWindow(self.hWnd, win32con.SW_SHOW)
48+
49+
def toggle(self):
50+
self.visible = not self.visible
51+
win32gui.ShowWindow(self.hWnd, win32con.SW_SHOW if self.visible else win32con.SW_HIDE)
52+
53+
def update(self, bounding_boxes):
54+
hdc = win32gui.GetDC(self.hWnd)
55+
pen = win32gui.CreatePen(win32con.PS_SOLID, 2, win32api.RGB(0, 255, 0))
56+
win32gui.SelectObject(hdc, pen)
57+
58+
for box in bounding_boxes:
59+
x1, y1, x2, y2 = box
60+
win32gui.Rectangle(hdc, x1, y1, x2, y2)
61+
62+
win32gui.DeleteObject(pen)
63+
win32gui.ReleaseDC(self.hWnd, hdc)
64+
65+
def _wnd_proc(self, hWnd, msg, wParam, lParam):
66+
if msg == win32con.WM_DESTROY:
67+
win32gui.PostQuitMessage(0)
68+
return win32gui.DefWindowProc(hWnd, msg, wParam, lParam)

0 commit comments

Comments
 (0)