Привет. В этой статье описано краткое руководство по созданию нейрочитов на основе CV YOLO11 на примере дота2. Панелька Chlenix_V8_turbo_SS Но начать нужно издалека... История Chlenix. На дворе был 2007 год. Блейзер, эмо, скейтеры, стена в вк... На все эти вещи команде NaVI было все равно. В это время они слишком заняты разносом кабин на арене, Маркелов делает минус 4 через мид на дасте 2 с дзигла, Эдвард с юспа делает легчайшие минус 5, в промежутках между матчами пересчитывая лаванде с призовых... Но мало кто знает, кто стоит за этим на самом деле... CHLENIX. Как был на самом деле сделан минус 5 с юспа ? Запуск членикса сопровождался уведомлением противника об использовании чита чтобы ввести противника в большую дизмораль. Реакция противника не заставляла себя долго ждать... Именно благодаря CHLENIX безизвестные парни из дервени стали чемпионами мира по cs1.6 Независимое расследование в области Chlenix. Это вдохновило меня создать новую версию Chelinx для дота2. Chlenix_V8_turbo_SS.Чит обучен смотреть на миникарту и давать SS. Теперь можно смело кидать всех в олмут и не думать о том что какой нибудь малолетний пуси дестроер не даст SS или вы его не увидите из за мута. Теперь будет видно все, каждое движение, всех, 9 уебков на миникарте. Если нейросеть определила SS, игрок получает уведомление в панельке + голосовое уведомление. В качестве CV используется YOLO обученная на своем датасете. Чит Chlenix_V8_turbo_SS в действии. Обучение CV YOLO. Датасет и обучение. На форуме уже есть прекрасная статья по обучению именно этой нейросети. Повторятся не буду. https://lolz.live/threads/6206682/ Единственное, что скажу, 150-200 эпох хватит, зачем вучаев указывает 2500 я хз если честно. Может быть у него датасет неебических размеров и самая жирная модель... Мы это уже вряд ли узнаем. Сперва нужно собирать датасет. Для этого нужно сделать скрины из последних 5-10 игр. Из каждой игры нужно по 50 скриншотов примерно. И очень важно указать следующие настройки для миникарты в доте. Основная проблема заключается в том что 2 героя на одной линии могут стоять очень близко, вследствие чего 1 из 2 героев может быть плохо видно и нейронка будет думать что на линии 1 перс. Для этого выставляется маленький размер героев на миникарте. Вам нужно в точности повторить все настройки как на скрине выше. Как только собрали датасет, то нужно обрезать все скрины в области миникарты. Для этого я воспользовался ffmpeg. В результате рядом появится папка со всеми миникартами. for %i in ("C:путь к датасету\*.png") do ffmpeg -i "%i" -vf "crop=280:280:0:1050" "C:путь к датасету\output\%~ni_output.png" Код for %i in ("C:путь к датасету\*.png") do ffmpeg -i "%i" -vf "crop=280:280:0:1050" "C:путь к датасету\output\%~ni_output.png" Далее запускаем обучение на 200 эпохах. Для скромного датасета этого должно хватить. from ultralytics import YOLO import multiprocessing def main(): model = YOLO("yolo11m.pt") results = model.train(data="data.yaml", epochs=200, imgsz=640) if __name__ == '__main__': main() Python from ultralytics import YOLO import multiprocessing def main(): model = YOLO("yolo11m.pt") results = model.train(data="data.yaml", epochs=200, imgsz=640) if __name__ == '__main__': main() imgsz=640 - параметр который подает датасет в разрешении 640*640. То есть у вас может быть миникарта в 4к, но в нейронку все будет подаваться в 640*640. imgsz всегда должен быть кратен 32! Это связано с архитектурой модели. В результате обучения будут получены веса и графики обучения. Чтобы понять че вы там научили открываете график results. Вы должны увидеть как графики loss идут вниз, если все так, то вы нормально обучили модель и можно приступать к запуску чита. Пример нормального обучения. Пример плохого обучения. Исходник и настройка Chlenix_V8_turbo_SS. Сам по себе чит достаточно простенький в реализации. Он работает только с миникартой. Область с вэбкамерой подается в нейронку, нейронка умеет детектить всех персов, которые находятся в зоне вижна по всей карте. Миникарта размечена областями top, mid, bot. Именно по этим областям чит ориентируется ушел враг с линии или нет. Если хотя бы 1 враг пропал с линии и его не видно в соответствующей области на протяжении 5 секунд, чит подает сигнал об SS и точно указывает линию + голосовое уведомление. Chlenix_V8_turbo_SS. import cv2 from ultralytics import YOLO from collections import defaultdict import time import threading import numpy as np import mss from playsound import playsound import os # Настройки MIN_ENEMY_TIME = 6 # сек отсутствия одного из врагов для триггера SS CONFIDENCE_THRESHOLD = 0.17 # Порог уверенности детекции INFO_PANEL_WIDTH = 300 # Ширина информационной панели AUDIO_FILES = { "top": "ssTop.mp3", "mid": "ssMid.mp3", "bot": "ssBot.mp3" } for zone, file in AUDIO_FILES.items(): if not os.path.exists(file): print(f"Аудиофайл {file} не найден!") # Инициализация model = YOLO("bestChlenix.pt") # Определите область для захвата миникарты MINIMAP_X, MINIMAP_Y, MINIMAP_W, MINIMAP_H = 0, 800, 280, 280 ZONES = { "top": {"x1": 10, "y1": 10, "x2": 80, "y2": 130, "max_enemies": 2}, "mid": {"x1": 90, "y1": 110, "x2": 170, "y2": 180, "max_enemies": 1}, "bot": {"x1": 190, "y1": 150, "x2": 278, "y2": 275, "max_enemies": 2} } zone_history = { zone: { "last_full_detection": 0, "current_count": 0, "ss_triggered": False, "warning_text": "", "warning_time": 0, "audio_played": False # Флаг для аудиоуведомлений } for zone in ZONES } COLORS = { "top": (0, 255, 0), "mid": (0, 165, 255), "bot": (0, 0, 255) } def play_audio(zone): try: playsound(AUDIO_FILES[zone]) except: print(f"Не удалось воспроизвести аудио для {zone}") def show_alert(zone): zone_history[zone]["warning_text"] = f"SS {zone.upper()}!" zone_history[zone]["warning_time"] = time.time() if not zone_history[zone]["audio_played"]: threading.Thread(target=play_audio, args=(zone,)).start() zone_history[zone]["audio_played"] = True def create_info_panel(height, width): panel = np.zeros((height, width, 3), dtype=np.uint8) cv2.putText(panel, "all mute SS checker", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1) return panel with mss.mss() as sct: while True: monitor = {"top": MINIMAP_Y, "left": MINIMAP_X, "width": MINIMAP_W, "height": MINIMAP_H} img = sct.grab(monitor) minimap = np.array(img) minimap = cv2.cvtColor(minimap, cv2.COLOR_BGRA2BGR) current_time = time.time() info_panel = create_info_panel(MINIMAP_H, INFO_PANEL_WIDTH) for zone, coords in ZONES.items(): color = COLORS[zone] cv2.rectangle(minimap, (coords["x1"], coords["y1"]), (coords["x2"], coords["y2"]), color, 1) cv2.putText(minimap, zone, (coords["x1"] + 5, coords["y1"] + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) results = model.predict(minimap, conf=CONFIDENCE_THRESHOLD) detections = results[0].boxes.data.cpu().numpy() current_counts = {zone: 0 for zone in ZONES} for det in detections: x1, y1, x2, y2, conf, cls = det cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 if cls == 0: # Класс врага!!! for zone, coords in ZONES.items(): if (coords["x1"] <= cx <= coords["x2"] and coords["y1"] <= cy <= coords["y2"]): current_counts[zone] += 1 for zone in ZONES: max_enemies = ZONES[zone]["max_enemies"] zone_history[zone]["current_count"] = current_counts[zone] if current_counts[zone] >= max_enemies: zone_history[zone]["last_full_detection"] = current_time zone_history[zone]["ss_triggered"] = False zone_history[zone]["warning_text"] = "" zone_history[zone]["audio_played"] = False elif (current_counts[zone] < max_enemies and current_time - zone_history[zone]["last_full_detection"] > MIN_ENEMY_TIME and not zone_history[zone]["ss_triggered"]): print(f"SS WARNING in {zone}!") threading.Thread(target=show_alert, args=(zone,)).start() zone_history[zone]["ss_triggered"] = True y_offset = 60 for zone in ZONES: count_text = f"{zone}: {current_counts[zone]}/{ZONES[zone]['max_enemies']}" color = COLORS[zone] cv2.putText(info_panel, count_text, (10, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 1) y_offset += 30 if (zone_history[zone]["warning_text"] and current_time - zone_history[zone]["warning_time"] < 3): cv2.putText(info_panel, zone_history[zone]["warning_text"], (10, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) y_offset += 30 detected_frame = results[0].plot() combined_frame = np.hstack((detected_frame, info_panel)) cv2.imshow("ZaicevPlusOdin_Chlenix_V8_turbo_SS", combined_frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows() Python import cv2 from ultralytics import YOLO from collections import defaultdict import time import threading import numpy as np import mss from playsound import playsound import os # Настройки MIN_ENEMY_TIME = 6 # сек отсутствия одного из врагов для триггера SS CONFIDENCE_THRESHOLD = 0.17 # Порог уверенности детекции INFO_PANEL_WIDTH = 300 # Ширина информационной панели AUDIO_FILES = { "top": "ssTop.mp3", "mid": "ssMid.mp3", "bot": "ssBot.mp3" } for zone, file in AUDIO_FILES.items(): if not os.path.exists(file): print(f"Аудиофайл {file} не найден!") # Инициализация model = YOLO("bestChlenix.pt") # Определите область для захвата миникарты MINIMAP_X, MINIMAP_Y, MINIMAP_W, MINIMAP_H = 0, 800, 280, 280 ZONES = { "top": {"x1": 10, "y1": 10, "x2": 80, "y2": 130, "max_enemies": 2}, "mid": {"x1": 90, "y1": 110, "x2": 170, "y2": 180, "max_enemies": 1}, "bot": {"x1": 190, "y1": 150, "x2": 278, "y2": 275, "max_enemies": 2} } zone_history = { zone: { "last_full_detection": 0, "current_count": 0, "ss_triggered": False, "warning_text": "", "warning_time": 0, "audio_played": False # Флаг для аудиоуведомлений } for zone in ZONES } COLORS = { "top": (0, 255, 0), "mid": (0, 165, 255), "bot": (0, 0, 255) } def play_audio(zone): try: playsound(AUDIO_FILES[zone]) except: print(f"Не удалось воспроизвести аудио для {zone}") def show_alert(zone): zone_history[zone]["warning_text"] = f"SS {zone.upper()}!" zone_history[zone]["warning_time"] = time.time() if not zone_history[zone]["audio_played"]: threading.Thread(target=play_audio, args=(zone,)).start() zone_history[zone]["audio_played"] = True def create_info_panel(height, width): panel = np.zeros((height, width, 3), dtype=np.uint8) cv2.putText(panel, "all mute SS checker", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 1) return panel with mss.mss() as sct: while True: monitor = {"top": MINIMAP_Y, "left": MINIMAP_X, "width": MINIMAP_W, "height": MINIMAP_H} img = sct.grab(monitor) minimap = np.array(img) minimap = cv2.cvtColor(minimap, cv2.COLOR_BGRA2BGR) current_time = time.time() info_panel = create_info_panel(MINIMAP_H, INFO_PANEL_WIDTH) for zone, coords in ZONES.items(): color = COLORS[zone] cv2.rectangle(minimap, (coords["x1"], coords["y1"]), (coords["x2"], coords["y2"]), color, 1) cv2.putText(minimap, zone, (coords["x1"] + 5, coords["y1"] + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 1) results = model.predict(minimap, conf=CONFIDENCE_THRESHOLD) detections = results[0].boxes.data.cpu().numpy() current_counts = {zone: 0 for zone in ZONES} for det in detections: x1, y1, x2, y2, conf, cls = det cx, cy = (x1 + x2) / 2, (y1 + y2) / 2 if cls == 0: # Класс врага!!! for zone, coords in ZONES.items(): if (coords["x1"] <= cx <= coords["x2"] and coords["y1"] <= cy <= coords["y2"]): current_counts[zone] += 1 for zone in ZONES: max_enemies = ZONES[zone]["max_enemies"] zone_history[zone]["current_count"] = current_counts[zone] if current_counts[zone] >= max_enemies: zone_history[zone]["last_full_detection"] = current_time zone_history[zone]["ss_triggered"] = False zone_history[zone]["warning_text"] = "" zone_history[zone]["audio_played"] = False elif (current_counts[zone] < max_enemies and current_time - zone_history[zone]["last_full_detection"] > MIN_ENEMY_TIME and not zone_history[zone]["ss_triggered"]): print(f"SS WARNING in {zone}!") threading.Thread(target=show_alert, args=(zone,)).start() zone_history[zone]["ss_triggered"] = True y_offset = 60 for zone in ZONES: count_text = f"{zone}: {current_counts[zone]}/{ZONES[zone]['max_enemies']}" color = COLORS[zone] cv2.putText(info_panel, count_text, (10, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 1) y_offset += 30 if (zone_history[zone]["warning_text"] and current_time - zone_history[zone]["warning_time"] < 3): cv2.putText(info_panel, zone_history[zone]["warning_text"], (10, y_offset), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) y_offset += 30 detected_frame = results[0].plot() combined_frame = np.hstack((detected_frame, info_panel)) cv2.imshow("ZaicevPlusOdin_Chlenix_V8_turbo_SS", combined_frame) if cv2.waitKey(1) & 0xFF == ord('q'): break cv2.destroyAllWindows() Все ключевые настройки вынес в начало кода. CONFIDENCE_THRESHOLD - Уверенность детекции. Если вдруг нейронка плохо детектит персов, то можно выкрутить пониже, я вообще указал 0.2 и теперь у меня нейросеть думает, что т2 на стороне света это союзник, но в целом это никак не роляет и все остальное детектится нормально. MIN_ENEMY_TIME - Время отсутствия противника для SS. 4-6 секунд норм, в другие диапазоны нет смысла лезть. Так же ниже в самом коде есть зоны определяющие какую область экрана подавать в нейросеть, какие области считать топом, мидом и ботом. В целом все очень наглядно и понятно поэтому подобрать сможете сами если мои настройки для вашего разрешения не подходят. Так же для голосовых уведомлений в директории с члениксом должны находиться 3 аудиофала в формате MP3 (WAV у меня почему то не заработал). Я их создал через openaiFM. И названия должны быть соответствующие. AUDIO_FILES = { "top": "ssTop.mp3", "mid": "ssMid.mp3", "bot": "ssBot.mp3" } Python AUDIO_FILES = { "top": "ssTop.mp3", "mid": "ssMid.mp3", "bot": "ssBot.mp3" } на 109 строчке есть if cls == 0: Тут нужно указать номер класса, указывающего на врага, это зависит от того как вы обучали нейросеть. Но если не помните, с помощью этого кода можно вывести все доступные классы и узнать их номера для конкретной модели. from ultralytics import YOLO model = YOLO('yolo11n-seg.pt') classes = model.names for idx, class_name in classes.items(): print(f"{idx}: {class_name}") Python from ultralytics import YOLO model = YOLO('yolo11n-seg.pt') classes = model.names for idx, class_name in classes.items(): print(f"{idx}: {class_name}") Видеокарта выше 55 градусов не нагрелась во время игры и работы чита. Так что если кому то будет интересно реализовать, что то похожее юзайте yolo. На CPU она тоже вполне нормально работает, но лучше на GPU.
SS? Ты что нацик? А если серьезно, то счастья здоровья скриптерам, которые дают оригинальные названия своим читам.