From 90a363b3e4100e5cccea27e3a3359b0eb8ead27c Mon Sep 17 00:00:00 2001 From: SunOne Date: Tue, 5 Mar 2024 17:33:33 +0300 Subject: [PATCH] version 0.4.9 --- README.md | 6 +++ config.ini | 6 +++ logic/config_watcher.py | 91 ++++++++++++++++++++++++-------------- logic/mouse.py | 8 ++-- run.py | 96 +++++++++++++++++++++++++++++++---------- version | 4 +- 6 files changed, 149 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 0cb84fa..bf9358d 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ The behavior of the aim bot can be configured via the [`config.ini`](https://git - hotkey_exit `str`: Exit. - hotkey_pause `str`: Pause AIM. - hotkey_reload_config `str`: Reload config. +- hotkey_turn_off_mask `str`: Turn off exclude mask. ### Mouse: - mouse_dpi `int`: Mouse DPI. @@ -110,6 +111,7 @@ The behavior of the aim bot can be configured via the [`config.ini`](https://git - show_overlay_detector `bool`: Show the detector overlay. - show_overlay_boxes `bool`: Show goals inside the overlay. - show_overlay_line `bool`: Show line from crosshair to target. +- show_overlay_mask `bool`: Show exclude mask. ### Debug window: - show_window `bool`: Shows the OpenCV2 window for visual feedback. @@ -123,6 +125,10 @@ The behavior of the aim bot can be configured via the [`config.ini`](https://git - debug_window_scale_percent `int`: Adjusts the size of the debug window. - debug_window_name `str`: Specifies the title of the debug window. +### Exclude mask: +- mask_enabled `bool`: Enable exclude mask. +- mask_points `str`: Saved points exclude mask. If you are playing from a third person, the character can be masked with a mask that will exclude him from targeting targets. Turn on the debugging window and select 4 points, the mask will be automatically saved in the config. + ## AI Models - *.pt: Default AI model. - *.onnx: The model is optimized to run on processors. diff --git a/config.ini b/config.ini index 3bd04d5..e40a8b8 100644 --- a/config.ini +++ b/config.ini @@ -21,6 +21,7 @@ hotkey_targeting = RightMouseButton hotkey_exit = F2 hotkey_pause = F3 hotkey_reload_config = F4 +hotkey_turn_off_mask = RightMouseButton [Mouse] mouse_dpi = 1000 @@ -53,6 +54,7 @@ AI_mouse_net = False show_overlay_detector = False show_overlay_boxes = True show_overlay_line = True +show_overlay_mask = True [Debug window] show_window = False @@ -65,3 +67,7 @@ show_target_line = True debug_window_always_on_top = True debug_window_scale_percent = 100 debug_window_name = Calculator + +[Exclude Mask] +mask_enabled = False +mask_points = (0, 0),(220, 106),(220, 420),(0, 420) \ No newline at end of file diff --git a/logic/config_watcher.py b/logic/config_watcher.py index c89c377..1c654e0 100644 --- a/logic/config_watcher.py +++ b/logic/config_watcher.py @@ -17,15 +17,16 @@ def Read(self, verbose=False): self.bettercam_capture_fps = int(self.config_Bettercam_Capture['bettercam_capture_fps']) self.bettercam_monitor_id = int(self.config_Bettercam_Capture['bettercam_monitor_id']) self.bettercam_gpu_id = int(self.config_Bettercam_Capture['bettercam_gpu_id']) + self.config_Obs_capture = self.config['Capture Methods'] self.Obs_capture = self.config_Obs_capture.getboolean('Obs_capture') self.Obs_camera_id = str(self.config_Obs_capture['Obs_camera_id']) self.Obs_capture_fps = int(self.config_Obs_capture['Obs_capture_fps']) # Aim - self.config_Aim_settings = self.config['Aim'] - self.body_y_offset = float(self.config_Aim_settings['body_y_offset']) - self.hideout_targets = self.config_Aim_settings.getboolean('hideout_targets') - self.disable_headshot = self.config_Aim_settings.getboolean('disable_headshot') + self.config_Aim = self.config['Aim'] + self.body_y_offset = float(self.config_Aim['body_y_offset']) + self.hideout_targets = self.config_Aim.getboolean('hideout_targets') + self.disable_headshot = self.config_Aim.getboolean('disable_headshot') # Hotkeys self.config_Hotkeys_settings = self.config['Hotkeys'] self.hotkey_targeting = str(self.config_Hotkeys_settings['hotkey_targeting']) @@ -33,37 +34,39 @@ def Read(self, verbose=False): self.hotkey_exit = str(self.config_Hotkeys_settings['hotkey_exit']) self.hotkey_pause = str(self.config_Hotkeys_settings['hotkey_pause']) self.hotkey_reload_config = str(self.config_Hotkeys_settings['hotkey_reload_config']) + self.hotkey_turn_off_mask = str(self.config_Hotkeys_settings['hotkey_turn_off_mask']) # Mouse - self.config_Mouse_settings = self.config['Mouse'] - self.mouse_dpi = int(self.config_Mouse_settings['mouse_dpi']) - self.mouse_sensitivity = float(self.config_Mouse_settings['mouse_sensitivity']) - self.mouse_fov_width = int(self.config_Mouse_settings['mouse_fov_width']) - self.mouse_fov_height = int(self.config_Mouse_settings['mouse_fov_height']) - self.mouse_lock_target = self.config_Mouse_settings.getboolean('mouse_lock_target') - self.mouse_auto_shoot = self.config_Mouse_settings.getboolean('mouse_auto_shoot') - self.mouse_auto_aim = self.config_Mouse_settings.getboolean('mouse_auto_aim') - self.mouse_ghub = self.config_Mouse_settings.getboolean('mouse_ghub') - self.mouse_triggerbot = self.config_Mouse_settings.getboolean('mouse_triggerbot') - self.mouse_force_click = self.config_Mouse_settings.getboolean('mouse_force_click') + self.config_Mouse = self.config['Mouse'] + self.mouse_dpi = int(self.config_Mouse['mouse_dpi']) + self.mouse_sensitivity = float(self.config_Mouse['mouse_sensitivity']) + self.mouse_fov_width = int(self.config_Mouse['mouse_fov_width']) + self.mouse_fov_height = int(self.config_Mouse['mouse_fov_height']) + self.mouse_lock_target = self.config_Mouse.getboolean('mouse_lock_target') + self.mouse_auto_shoot = self.config_Mouse.getboolean('mouse_auto_shoot') + self.mouse_auto_aim = self.config_Mouse.getboolean('mouse_auto_aim') + self.mouse_ghub = self.config_Mouse.getboolean('mouse_ghub') + self.mouse_triggerbot = self.config_Mouse.getboolean('mouse_triggerbot') + self.mouse_force_click = self.config_Mouse.getboolean('mouse_force_click') # Arduino - self.config_Arduino_settings = self.config['Arduino'] - self.arduino_move = self.config_Arduino_settings.getboolean('arduino_move') - self.arduino_shoot = self.config_Arduino_settings.getboolean('arduino_shoot') - self.arduino_port = str(self.config_Arduino_settings['arduino_port']) - self.arduino_baudrate = int(self.config_Arduino_settings['arduino_baudrate']) + self.config_Arduino = self.config['Arduino'] + self.arduino_move = self.config_Arduino.getboolean('arduino_move') + self.arduino_shoot = self.config_Arduino.getboolean('arduino_shoot') + self.arduino_port = str(self.config_Arduino['arduino_port']) + self.arduino_baudrate = int(self.config_Arduino['arduino_baudrate']) # AI - self.config_AI_options = self.config['AI'] - self.AI_model_name = str(self.config_AI_options['AI_model_name']) - self.AI_image_size = int(self.config_AI_options['AI_image_size']) - self.AI_conf = float(self.config_AI_options['AI_conf']) - self.AI_device = str(self.config_AI_options['AI_device']) - self.AI_enable_AMD = self.config_AI_options.getboolean('AI_enable_AMD') - self.AI_mouse_net = self.config_AI_options.getboolean('AI_mouse_net') + self.config_AI = self.config['AI'] + self.AI_model_name = str(self.config_AI['AI_model_name']) + self.AI_image_size = int(self.config_AI['AI_image_size']) + self.AI_conf = float(self.config_AI['AI_conf']) + self.AI_device = str(self.config_AI['AI_device']) + self.AI_enable_AMD = self.config_AI.getboolean('AI_enable_AMD') + self.AI_mouse_net = self.config_AI.getboolean('AI_mouse_net') # Overlay - self.config_Overlay_detector = self.config['Overlay'] - self.show_overlay_detector = self.config_Overlay_detector.getboolean('show_overlay_detector') - self.show_overlay_boxes = self.config_Overlay_detector.getboolean('show_overlay_boxes') - self.show_overlay_line = self.config_Overlay_detector.getboolean('show_overlay_line') + self.config_Overlay = self.config['Overlay'] + self.show_overlay_detector = self.config_Overlay.getboolean('show_overlay_detector') + self.show_overlay_boxes = self.config_Overlay.getboolean('show_overlay_boxes') + self.show_overlay_line = self.config_Overlay.getboolean('show_overlay_line') + self.show_overlay_mask = self.config_Overlay.getboolean('show_overlay_mask') # Debug window self.config_Debug_window = self.config['Debug window'] self.show_window = self.config_Debug_window.getboolean('show_window') @@ -76,6 +79,30 @@ def Read(self, verbose=False): self.debug_window_always_on_top = self.config_Debug_window.getboolean('debug_window_always_on_top') self.debug_window_scale_percent = int(self.config_Debug_window['debug_window_scale_percent']) self.debug_window_name = str(self.config_Debug_window['debug_window_name']) - + # Exclude Detection Mask + self.config_Exclude_Mask = self.config['Exclude Mask'] + self.mask_enabled = self.config_Exclude_Mask.getboolean('mask_enabled') + self.mask_points = str(self.config_Exclude_Mask['mask_points']).split(',') + if verbose: print('Config reloaded') + + def save_mask_points(self, mask_points): + self.config.set('Exclude Mask', 'mask_points', ','.join(map(str, mask_points))) + with open('./config.ini', 'w') as configfile: + self.config.write(configfile) + + def read_mask_points(self): + mask_points_str = self.config.get('Exclude Mask', 'mask_points', fallback='') + mask_points = [] + try: + points = mask_points_str.replace(' ', '').split('),(') + points[0] = points[0].lstrip('(') + points[-1] = points[-1].rstrip(')') + for point_str in points: + point = tuple(map(int, point_str.split(','))) + mask_points.append(point) + return mask_points + except Exception as e: + print(f'Invalid mask points format in config. Should be (x1, y1),(x2, y2),...\n{e}') + return [] \ No newline at end of file diff --git a/logic/mouse.py b/logic/mouse.py index c2eb26e..7f7bbc7 100644 --- a/logic/mouse.py +++ b/logic/mouse.py @@ -1,7 +1,5 @@ -import math import queue import threading -import time import torch import win32con, win32api from ctypes import * @@ -90,9 +88,9 @@ def mouse_close(self): class Mouse_net(nn.Module): def __init__(self, arch): super(Mouse_net, self).__init__() - self.fc1 = nn.Linear(in_features=10, out_features=64, device=f'{arch}') - self.fc2 = nn.Linear(in_features=64, out_features=64, device=f'{arch}') - self.fc3 = nn.Linear(in_features=64, out_features=2, device=f'{arch}') + self.fc1 = nn.Linear(in_features=10, out_features=64, device=arch) + self.fc2 = nn.Linear(in_features=64, out_features=64, device=arch) + self.fc3 = nn.Linear(in_features=64, out_features=2, device=arch) def forward(self, x): x = torch.relu(self.fc1(x)) diff --git a/run.py b/run.py index 287ce59..392e880 100644 --- a/run.py +++ b/run.py @@ -5,6 +5,7 @@ from logic.mouse import MouseThread from ultralytics import YOLO +import numpy as np import math import torch import cv2 @@ -13,6 +14,10 @@ import win32api, win32con, win32gui if cfg.show_overlay_detector: import tkinter as tk + +mask_points = [] +annotated_frame = None +global_mask = None class Target: def __init__(self, x, y, w, h, cls): @@ -76,42 +81,63 @@ def print_startup_messages(): f'[{cfg.hotkey_targeting}] - Aiming at the target\n', f'[{cfg.hotkey_exit}] - EXIT\n', f'[{cfg.hotkey_pause}] - PAUSE AIM\n', - f'[{cfg.hotkey_reload_config}] - Reload config\n') + f'[{cfg.hotkey_reload_config}] - Reload config\n', + f'[{cfg.hotkey_turn_off_mask}] - Disable exclude mask\n') def process_hotkeys(cfg_reload_prev_state): global app_pause global clss + global mask_active app_pause = win32api.GetKeyState(Buttons.KEY_CODES[cfg.hotkey_pause]) app_reload_cfg = win32api.GetKeyState(Buttons.KEY_CODES[cfg.hotkey_reload_config]) + mask_active = win32api.GetAsyncKeyState(Buttons.KEY_CODES[cfg.hotkey_turn_off_mask]) + mask_points = cfg.read_mask_points() if app_reload_cfg != cfg_reload_prev_state: if app_reload_cfg in (1, 0): cfg.Read(verbose=True) frames.reload_capture() mouse_worker.Update_settings() clss = active_classes() - if cfg.show_window == False: cv2.destroyAllWindows() cfg_reload_prev_state = app_reload_cfg - return cfg_reload_prev_state + return cfg_reload_prev_state, mask_points def update_overlay_window(overlay): if cfg.show_overlay_detector: overlay.overlay_detector.update() overlay.canvas.delete("all") - + +def create_mask_from_points(image_shape, points): + mask = np.zeros([image_shape[1], image_shape[0]], dtype=np.uint8) + pts = np.array([points], dtype=np.int32) + cv2.fillPoly(mask, pts, 255) + return mask + +def debug_window_click_handler(event, x, y, flags, param): + global mask_points + global global_mask + if event == cv2.EVENT_LBUTTONDOWN: + mask_points.append((x, y)) + if len(mask_points) == 4: + global_mask = create_mask_from_points(param, mask_points) + cfg.save_mask_points(mask_points) + print('mask created:', mask_points) + mask_points = [] + def spawn_debug_window(): if cfg.show_window: cv2.namedWindow(cfg.debug_window_name) + cv2.setMouseCallback(cfg.debug_window_name, debug_window_click_handler, [cfg.detection_window_width, cfg.detection_window_height]) if cfg.debug_window_always_on_top: debug_window_hwnd = win32gui.FindWindow(None, cfg.debug_window_name) win32gui.SetWindowPos(debug_window_hwnd, win32con.HWND_TOPMOST, 100, 100, 200, 200, 0) -def sort_targets(frame, cfg, arch) -> List[Target]: - boxes_array = frame.boxes.xywh.to(f'{arch}') - distances_sq = torch.sum((boxes_array[:, :2] - torch.tensor([frames.screen_x_center, frames.screen_y_center], device=f'{arch}')) ** 2, dim=1) - classes_tensor = frame.boxes.cls.to(f'{arch}') +def sort_targets(frame, cfg, arch, mask) -> List[Target]: + boxes_array = frame.boxes.xywh.to(arch) + distances_sq = torch.sum((boxes_array[:, :2] - torch.tensor([frames.screen_x_center, frames.screen_y_center], device=arch)) ** 2, dim=1) + classes_tensor = frame.boxes.cls.to(arch) if not cfg.disable_headshot: score = distances_sq + 10000 * (classes_tensor != 7).float() @@ -125,15 +151,23 @@ def sort_targets(frame, cfg, arch) -> List[Target]: sort_heads = torch.argsort(heads_distances_sq) heads = heads[sort_heads] else: - sort_heads = torch.tensor([], dtype=torch.int64, device=f'{arch}') + sort_heads = torch.tensor([], dtype=torch.int64, device=arch) other_distances_sq = distances_sq[other] sort_indices_other = torch.argsort(other_distances_sq) sort_indices = torch.cat((heads, other[sort_indices_other])).cpu().numpy() - - return [Target(*boxes_array[i, :4].cpu().numpy(), classes_tensor[i].item()) for i in sort_indices] - + + if mask is not None and cfg.mask_enabled and mask_active != -32768: + targets = [] + for i in sort_indices: + target = Target(*boxes_array[i, :4].cpu().numpy(), classes_tensor[i].item()) + if mask[int(target.y), int(target.x)] == 0: + targets.append(target) + return targets + else: + return [Target(*boxes_array[i, :4].cpu().numpy(), classes_tensor[i].item()) for i in sort_indices] + def active_classes() -> List[int]: clss = [0, 1] @@ -146,26 +180,34 @@ def active_classes() -> List[int]: return clss def init(): - arch = f'cuda:{cfg.AI_device}' + global annotated_frame + prev_frame_time, new_frame_time = 0, 0 if cfg.show_window and cfg.show_fps else None + cfg_reload_prev_state = 0 + shooting_queue = [] + spawn_debug_window() + if cfg.AI_enable_AMD: arch = f'hip:{cfg.AI_device}' + else: + arch = f'cuda:{cfg.AI_device}' if 'cpu' in cfg.AI_device: arch = 'cpu' - prev_frame_time, new_frame_time = 0, 0 if cfg.show_window and cfg.show_fps else None try: model = YOLO(f'models/{cfg.AI_model_name}', task='detect') print_startup_messages() except Exception as e: - print(e) + print('Loading model error:\n', e) quit(0) - spawn_debug_window() - cfg_reload_prev_state = 0 - shooting_queue = [] - + global global_mask + if cfg.mask_enabled: + mask_points = cfg.read_mask_points() + if mask_points: + global_mask = create_mask_from_points([cfg.detection_window_width, cfg.detection_window_height], mask_points) + while True: - cfg_reload_prev_state = process_hotkeys(cfg_reload_prev_state) + cfg_reload_prev_state, mask_points = process_hotkeys(cfg_reload_prev_state) image = frames.get_new_frame() result = perform_detection(model, image, clss) update_overlay_window(overlay) if cfg.show_overlay_detector else None @@ -177,16 +219,22 @@ def init(): if cfg.show_window and cfg.show_speed == True: annotated_frame = speed(annotated_frame, frame.speed['preprocess'], frame.speed['inference'], frame.speed['postprocess']) + if cfg.show_overlay_mask and cfg.show_overlay_detector and cfg.mask_enabled: + for i in range(len(mask_points)): + current_point = mask_points[i] + next_point = mask_points[(i + 1) % len(mask_points)] + overlay.canvas.create_line(current_point[0], current_point[1], next_point[0], next_point[1], width=2, fill='red') + if len(frame.boxes): if app_pause == 0: - shooting_queue = sort_targets(frame, cfg, arch) + shooting_queue = sort_targets(frame, cfg, arch, global_mask) if shooting_queue: target = shooting_queue[0] shooting_queue.clear() mouse_worker.queue.put((target.x, target.y, target.w, target.h)) - + if cfg.show_window or cfg.show_overlay_detector: screen_x_center = cfg.detection_window_width / 2 screen_y_center = cfg.detection_window_height / 2 @@ -209,7 +257,6 @@ def init(): if cfg.show_overlay_line: overlay.canvas.create_line(screen_x_center, screen_y_center, target.x, target.y + cfg.body_y_offset / target.h, width=2, fill='red') - else: pass if cfg.show_window and cfg.show_boxes: @@ -225,6 +272,9 @@ def init(): cv2.putText(annotated_frame, f'FPS: {str(int(fps))}', (10, 80) if cfg.show_speed else (10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1, cv2.LINE_AA) if cfg.show_window: + if cfg.mask_enabled: + mask_color = cv2.cvtColor(global_mask, cv2.COLOR_GRAY2BGR) + annotated_frame[global_mask > 0] = cv2.addWeighted(annotated_frame[global_mask > 0], 0.5, mask_color[global_mask > 0], 0.5, 0) try: if cfg.debug_window_scale_percent != 100: height = int(cfg.detection_window_height * cfg.debug_window_scale_percent / 100) diff --git a/version b/version index 530f1b3..497ccb0 100644 --- a/version +++ b/version @@ -1,2 +1,2 @@ -app=0.4.8 -config=41 \ No newline at end of file +app=0.4.9 +config=42 \ No newline at end of file