forked from Passer1072/RookieAI_yolov8
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathRookieAI_YOLOv8.py
2766 lines (2383 loc) · 125 KB
/
RookieAI_YOLOv8.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import gc
import json
import os
import threading
import time
import sys
import requests
import base64
import tkinter as tk
import webbrowser
import mouse
import dxcam
import psutil
from math import sqrt
from tkinter import filedialog, messagebox
from PIL import Image, ImageTk
from multiprocessing import freeze_support
import cv2
import numpy as np
import pyautogui
from typing import Union, Any
import win32api
import win32con
import customtkinter as ctk
from mss import mss
from ultralytics import YOLO
import ctypes
import string
import random
# ------------------------------------------判断AMD显卡-----------------------------------------------------------------
import torch
if not torch.cuda.is_available():
torch.backends.cudnn.enabled = False
# ------------------------------------------类声明----------------------------------------------------------------------
class AutoFire:
def __init__(self, interval=0.2):
self.interval = interval
self.running = False
self.destroyed = False
self.thread = threading.Thread(target=self._mouse_press_loop)
self.thread.daemon = True
self.thread.start()
def _mouse_press_loop(self):
"""循环监听"""
while not self.destroyed:
if self.running:
match mouse_control:
case '飞易来USB':
dll.M_KeyDown2(ctypes.c_uint64(hdl), int(1)) # 左键按下
dll.M_KeyDown2(ctypes.c_uint64(hdl), int(2)) # 左键抬起
pass
case 'win32':
win32api.mouse_event(
win32con.MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0)
win32api.mouse_event(
win32con.MOUSEEVENTF_LEFTUP, 0, 0, 0, 0)
case 'mouse':
mouse.click('left')
case 'Logitech':
LG_driver.click_Left_down()
LG_driver.click_Left_up()
pass
time.sleep(self.interval)
else:
time.sleep(0.1)
def start(self):
"""开始开火"""
self.running = True
def stop(self):
"""停止开火"""
self.running = False
def destroy(self):
"""释放资源"""
self.running = False
self.destroyed = True
self.thread = None
class Option:
def __init__(self):
self.default = {
'aimbot': True,
'lockSpeed': 0.7,
'triggerType': "按下",
'arduinoMode': False,
'lockKey': "右键",
'mouse_Side_Button_Witch': True,
'method_of_prediction': "倍率预测",
'confidence': 0.5,
# 'extra_offset_x': 5,
# 'extra_offset_y': 5,
'prediction_factor': 0.5,
'closest_mouse_dist': 160,
'screen_width': 360,
'screen_height': 360,
'aimOffset': 0.4,
'aimOffset_Magnification_x': 0,
'model_file': "yolov8n.pt",
'test_window_frame': False,
"crawl_information": True,
"screenshot_mode": False,
"DXcam_screenshot": 360,
'dxcam_maxFPS': 30,
'segmented_aiming_switch': False,
'mouse_control': 'win32',
'stage1_scope': 50,
'stage1_intensity': 0.8,
'stage2_scope': 170,
'stage2_intensity': 0.4,
"enable_random_offset": False,
"time_interval": 1,
"tolerance": 63.0,
"ignore_colors": [[62, 203, 236]],
"offset_range": [0, 1],
"recoil_switch": False,
"recoil_interval": 0.1,
"recoil_boosted_distance": 5,
"recoil_boosted_distance_time": 0.5,
"recoil_standard_distance": 1,
"recoil_transition_time": 0.2,
}
self.content = self.read()
def read(self) -> dict:
try:
with open("settings.json", "r", encoding='utf-8') as f:
return json.load(f)
except FileNotFoundError:
return self.default
def get(self, key: str, default: Any = None) -> Union[int, str, list, float, bool]:
"""
获取配置项的值,如果不存在则返回默认值。
:param key: 配置项的键
:param default: 默认值
:return: 返回配置项的值,类型可能是 int, str, list, float 或 bool
"""
if default is not None:
return self.content.get(key, default)
return self.content.get(key, self.default.get(key))
def update(self, key: str, value: Any) -> None:
self.content[key] = value
self.save()
def save(self) -> None:
with open("settings.json", "w", encoding='utf8') as f:
f.write(json.dumps(self.content, ensure_ascii=False, indent=4))
# ------------------------------------------函数声明---------------------------------------------------------------------
# 根据按键名称获取对应的虚拟键码
def get_lock_key(key: str) -> int | None:
KEY_MAP = {
'左键': 0x01,
'右键': 0x02,
'下侧键': 0x05,
'左Ctrl': 0xA2,
'右Ctrl': 0xA3,
'左Shift': 0xA0,
'右Shift': 0xA1,
'左Alt': 0xA4,
'右Alt': 0xA5,
}
return KEY_MAP.get(key, None)
# ------------------------------------------全局变量---------------------------------------------------------------------
# 选择模型
model_file = "yolov8n.pt"
# 新建一个 MSS 对象(获取截图)
sct = mss()
# 新建一个Option对象
Opt = Option()
# 新建一个AutoFire对象
AFe = AutoFire()
# returns a DXCamera instance on primary monitor
# Primary monitor's BetterCam instance
camera = dxcam.create(output_idx=0, output_color="BGR", max_buffer_len=2048)
# 截图模式(请勿更改)
screenshot_mode = False
# MSS默认截图长宽(像素)
screen_width = 640
screen_height = 640
# DXcam截图分辨率
DXcam_screenshot = 360
# 初始化帧数计时器(帧数计算)
frame_counter = 0
start_time = time.time()
start_test_time = time.time()
last_console_update = time.time()
last_gui_update = time.time()
last_screenshot_mode_update = time.time()
# 初始化gc计时器(垃圾清理)
gc_time = time.time()
# 初始化DXcam最大FPS
dxcam_maxFPS = 30
deactivate_dxcam = False # 是否禁止加载dxcam
# onnx模型开关
half_precision_model = False
# 颜色忽略部分
tolerance = 80 # Default value
ignore_colors = [[62, 203, 236]] # Default value, list of BGR tuples
# 自瞄范围
closest_mouse_dist = 100
# 置信度设置
confidence = 0.65
# 垂直瞄准偏移
aimOffset = 0.5
# 停止自瞄范围(百分比)
stop_aim = 1
stop_aim_variety = None # 变化值
stop_aim_target = 0 # 目标值
stop_aim_variety_state = True # 停止自瞄范围是否需要回到默认值
slow_aim_speed = 0.5 # 初始化慢速(停止)瞄准数值
expansion_ratio = 1.5 # 扩张强锁范围面积%
# 水平瞄准偏移。最左为1,最右为-1,中间(默认)为0,可为小数
aimOffset_Magnification_x = 0
# 水平瞄准偏移
aimOffset_x = 0
# 预测因子
prediction_factor = 0.1
# 跟踪目标的上一个位置(在程序运行中保存和更新)
previous_centerx = 0
previous_centery = 0
# 鼠标移动平滑
mouse_movement_smoothing_switch = False # 鼠标移动平滑开关
threshold = 2.0 # threshold: 检测目标是否“停止”的速度阈值。
slowDownFactor = 0.3 # slowDownFactor: 目标停止时的减速因子。
# 预设的阈值和参数
reverse_threshold_x = 4 # 短时间内反向移动的阈值 x 轴
reverse_threshold_y = 2 # 短时间内反向移动的阈值 y 轴
smoothing_factor = 0.6 # 平滑因子,接近1表示更平滑的跟踪
# 新倍率预测:记录上一个方向的目标偏移量和时间
previous_direction = None
transition_start_x = None
transition_duration = 10 # 过渡时间(帧数或时间单位)
direction = "静止"
coordinate_movement_switch = False # 坐标移动模式开关
# 定义稀疏流光缓存用于存储5次推理结果
direction_cache = []
# 软件页面大小
_win_width = 350
_win_height = 825
# 识别对象限制(敌我识别)
classes = 0
# 锁定对象ID
locked_id = None
# 分阶段瞄准
stage1_scope = 55 # 强锁范围
stage1_intensity = 0.8 # 强锁力度
stage2_scope = 170 # 软锁范围
stage2_intensity = 0.4 # 软锁力度
# 初始化压枪参数
recoil_switch = False # 压枪开关
recoil_interval = 0.01 # 压枪间隔(s)
recoil_boosted_distance_time = 0.5 # 一阶段时间
recoil_boosted_distance = 4 # 一阶段力度
recoil_standard_distance = 1 # 二阶段力度
recoil_transition_time = 0.2 # 一阶段到二阶段缓冲时间
# 跟踪框ID相关变量
counter_id = 0 # 分配唯一 ID 的计数器
buffer_tracks = {} # 保存位置和时间的元组
last_updated = {} # 初始化 last_updated 字典
identification_range = 30 # 连续框判断误差(像素)
unupdated_timeout = 0.1 # 未更新的轨迹存在时间(秒)
crossed_boxes = {}
# 分段瞄准开关
segmented_aiming_switch = False
# 是否开启外部测试窗口(小幅影响性能)
test_window_frame = False
# 是否打开内部测试画面(大幅影响性能)
test_images_GUI = False
# 是否跳过公告获取
crawl_information = False
# 初始化加载成功标识
loaded_successfully = False
# 自动扳机开关
automatic_Trigger = False
# 选择鼠标控制方式
mouse_control = 'win32'
# 飞易来U盘设置
u_vid = 0x1532
u_pid = 0x98
# 目标列表
num_classes = 10 # 假设模型识别10个类别
target_all = list(range(num_classes))
target_mapping = {'敌人': 0, '倒地': 1, '队友': 2, '全部': target_all}
# 人体示意图
img = Image.open("body_photo.png")
# 定义触发器类型和其他参数
# 在全局范围声明 GUI 控件的变量
aimbot_var = None
circle_var = None
lockSpeed_scale = None
triggerType_var = None
arduinoMode_var = None
lockKey_var = None
confidence_scale = None
closest_mouse_dist_scale = None
screen_width_scale = None
screen_height_scale = None
root = None
aimOffset_scale = None
mouse_Side_Button_Witch_var = None
value_label = None
LookSpeed_label_text = None
target_selection = None
target_selection_var = None
target_selection_str = None
method_of_prediction_var = None
readme_content = ""
random_offset_mode_var = None
# 其他全局变量
Thread_to_join = None
restart_thread = False
run_threads = True
draw_center = True
random_name = False
dll_lg_loaded = False # 初始化lg_dll加载标识符
recoil_start_time = None
# 随机偏移Y轴状态变量
current_target = None
current_target_dist = float('inf')
aim_offset_x = 0
aim_offset_y = 0
offset_range = (0, 1) # 偏移范围
time_interval = 0.5 # 时间间隔,单位:秒
last_offset_time = time.time()
last_recoil_time = time.time() # 压枪间隔时间初始化
enable_random_offset = False # 随机偏移功能开关
# ------------------------------------------def部分---------------------------------------------------------------------
def random_string(length): # 随即软件名称
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for _ in range(length))
def calculate_screen_monitor(capture_width=640, capture_height=640): # 截图区域
# 获取屏幕的宽度和高度
screen_width, screen_height = pyautogui.size()
# 计算中心点坐标
center_x, center_y = screen_width // 2, screen_height // 2
# 定义截图区域,以中心点为基准,截取一个 capture_width x capture_height 的区域
monitor = {
"top": center_y - capture_height // 2,
"left": center_x - capture_width // 2,
"width": capture_width,
"height": capture_height,
}
return monitor
def calculate_frame_rate(frame_counter, start_time, end_time): # 帧率计算
# 避免被零除
if end_time - start_time != 0:
frame_rate = frame_counter / (end_time - start_time)
# 重置下一秒的frame_counter和start_time
frame_counter = 0
start_time = time.time()
else:
frame_rate = 0 # Or assign something that makes sense in your case
return frame_rate, frame_counter, start_time
def update_and_display_fps(frame_, frame_counter, start_time, end_time):
global last_console_update, last_gui_update
frame_counter += 1
frame_rate, frame_counter, start_time = calculate_frame_rate(
frame_counter, start_time, end_time)
# 每2秒在控制台打印帧率
current_time = time.time()
if current_time - last_console_update > 2:
print(f"FPS: {frame_rate:.0f}") # 在控制台打印帧率
last_console_update = current_time
# 每1秒在图形用户界面上更新帧率
if current_time - last_gui_update > 0.5:
text_fps = "实时FPS:{:.0f}".format(frame_rate)
image_label_FPSlabel.configure(text=text_fps)
last_gui_update = current_time
# 在 cv2 窗口中继续显示帧率
cv2.putText(frame_, f"FPS: {frame_rate:.0f}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
return frame_, frame_counter, start_time
def capture_screen(monitor, sct):
# 使用 MSS 来抓取屏幕
screenshot = sct.grab(monitor)
# 把 PIL/Pillow Image 转为 OpenCV ndarray 对象,然后从 BGR 转换为 RGB
frame = np.array(screenshot)[:, :, :3]
return frame
def DXcam():
# 获取屏幕的宽度和高度
screen_width, screen_height = pyautogui.size()
# 计算截图区域
left, top = (
screen_width - DXcam_screenshot) // 2, (screen_height - DXcam_screenshot) // 2
right, bottom = left + DXcam_screenshot, top + DXcam_screenshot
region = (left, top, right, bottom)
camera.start(region=region, video_mode=True,
target_fps=dxcam_maxFPS) # 用于捕获区域的可选参数
def display_debug_window(frame): # 调试窗口
# 在主循环中显示图像
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('.'):
cv2.destroyAllWindows()
return True
else:
return False
def get_desired_size(screen_width_1, screen_height_1):
# 根据屏幕尺寸判断调整的大小
if screen_width_1 == 1920 and screen_height_1 == 1080:
desired_size = (300, 300)
elif screen_width_1 >= 2560 and screen_height_1 >= 1440:
desired_size = (370, 370)
else:
desired_size = (300, 300) # 默认大小
return desired_size
def fetch_readme(): # 从github更新公告
print("开始获取公告......")
try:
readme_url = "https://api.github.com/repos/Passer1072/RookieAI_yolov8/readme"
response = requests.get(readme_url, timeout=10)
response_text = base64.b64decode(
response.json()['content']).decode('utf-8')
print("获取成功")
# 找到 "更新日志:" 在字符串中的位置
update_log_start = response_text.find("更新日志:")
# 若找不到 "更新日志:",则返回全部内容
if update_log_start == -1:
return response_text
# 截取 "更新日志:" 及其后的所有文本
update_log = response_text[update_log_start:]
return update_log
except Exception as e:
print("获取失败:", e)
return "无法加载最新的 README 文件,这可能是因为网络问题或其他未知错误。"
def fetch_readme_version_number(): # 从github更新公告
print("开始获取版本号......")
try:
readme_url = "https://api.github.com/repos/Passer1072/RookieAI_yolov8/readme"
response = requests.get(readme_url, timeout=10)
response_text = base64.b64decode(
response.json()['content']).decode('utf-8')
print("获取成功")
# 创建搜索字符串
search_str = "Current latest version:"
# 找到 "更新日志:" 在字符串中的位置
update_log_start = response_text.find(search_str)
# 截取 "Current latest version: " 及其后的所有文本
# Move the index to the end of "Current latest version: "
update_log_start += len(search_str)
update_log = response_text[update_log_start:]
# 使用 strip 方法去除两侧空格
update_log = update_log.strip()
# 检查获取到的版本号的长度
if len(update_log) > 20:
return "版本号格式化错误"
else:
return update_log
except Exception as e:
print("获取失败:", e)
return "版本号获取失败"
# 加载DLL文件
def load_DLL(): # 加载易键鼠DLL
global dll, hdl, startup_successful
# 加载DLL文件
print("开始加载飞易来盒子文件...")
dll = ctypes.windll.LoadLibrary('./x64_msdk.dll') # 加载DLL
print("启动飞易来盒子...")
dll.M_Open_VidPid.restype = ctypes.c_uint64 # 声明M_Open函数的返回类型为无符号整数
hdl = dll.M_Open_VidPid(u_vid, u_pid) # 打开端口代码
print("open handle = " + str(hdl)) # 打开端口并打印端口开启状态
# 盒子启动测试
# print("鼠标移动:" + str(dll.M_MoveR(ctypes.c_uint64(hdl), 100, 100) == 0)) # 相对移动
startup_successful = True # 初始化启动状态
if dll.M_MoveR(ctypes.c_uint64(hdl), 100, 100) == 0:
print("鼠标移动:True")
print("启动盒子成功")
startup_successful = True # 纪律启动状态为True
else:
print("鼠标移动:False")
print("启动盒子失败")
startup_successful = False # 记录启动状态为False
def load_lg_dll(): # 加载罗技移动dll
global LG_driver
global dll_lg_loaded
dll_path = r'.\MouseControl.dll'
if os.path.exists(dll_path):
LG_driver = ctypes.CDLL(dll_path)
# 相对移动测试
LG_driver.move_R(100, 100)
dll_lg_loaded = True
else:
dll_lg_loaded = False
def check_Ubox_startup():
if not startup_successful:
print("未检测到盒子或盒子启动失败无法使用")
messagebox.showerror("错误", "未检测到盒子或盒子启动失败无法使用")
mouseMove_var.set('win32')
def check_Logitech_startup():
global dll_loaded
if not dll_loaded:
messagebox.showerror("错误", "未找到罗技MouseControl.dll文件")
mouseMove_var.set('win32')
def crawl_information_by_github():
global readme_content, readme_version
if crawl_information:
# 读取在线公告
readme_content = fetch_readme()
readme_version = fetch_readme_version_number()
def open_web(event):
webbrowser.open('https://github.com/Passer1072/RookieAI_yolov8') # 要跳转的网页
def string_to_list(s: str) -> list: # 忽略颜色保存样式格式化
s = s.replace('[', '').replace(']', '')
return [int(num_str) for num_str in s.split(',')]
def choose_model(): # 选择模型
global model_file
model_file = filedialog.askopenfilename() # 在这里让用户选择文件
model_file_label.config(text=model_file) # 更新标签上的文本为选择的文件路径
def open_settings_config():
os.startfile("settings.json")
def load_model_file(): # 加载模型文件
# 默认的模型文件地址
default_model_file = "yolov8n.pt"
model_file = Opt.get('model_file', default_model_file)
# 检查文件是否存在,如果不存在,使用默认模型文件
if not os.path.isfile(model_file):
print("[WARNING] 设置文件中的模型文件路径无效; 使用默认模型文件")
model_file = default_model_file
# 检测文件后缀名并设置 half_precision_model
file_extension = os.path.splitext(model_file)[1].lower() # 获取文件扩展名并转换为小写
if file_extension == ".onnx":
half_precision_model = False
elif file_extension in [".pt", ".engine"]:
half_precision_model = True
else:
half_precision_model = False # 默认情况下,非 .onnx 文件将 half_precision_model 设置为 False
print(f"加载模型文件: {model_file}")
print(f"半精度推理 设置为: {half_precision_model}")
# 如果 model_file 为 None 或者空,我们返回 None,否则我们返回对应的 YOLO 模型
return YOLO(model_file) if model_file else None
def track_box_id(centerx, centery, box_width, box_height, monitor): # 跟踪框ID
global counter_id, buffer_tracks, last_updated, crossed_boxes
# 计算框的面积
box_area = box_width * box_height
# 根据框的面积动态调整identification_range,最大100最小5
min_range = 90
max_range = 100
identification_range = min_range + \
(max_range - min_range) * (box_area /
(monitor["width"] * monitor["height"]))
box_id = None
for id, past_positions in buffer_tracks.items():
if len(past_positions) > 0 and np.linalg.norm(
past_positions[-1][0] - np.array([centerx, centery])) < identification_range:
box_id = id
break
if box_id is None:
box_id = counter_id
counter_id += 1
buffer_tracks[box_id] = []
crossed_boxes[box_id] = False
buffer_tracks[box_id].append((np.array([centerx, centery]), time.time()))
last_updated[box_id] = time.time()
return box_id
def count_pixels_of_color(image, target_color, tolerance=30): # 颜色忽略
target_color_bgr = target_color[::-1]
lower_bound = np.array([max(0, c - tolerance)
for c in target_color_bgr], dtype=np.uint8)
upper_bound = np.array([min(255, c + tolerance)
for c in target_color_bgr], dtype=np.uint8)
mask = cv2.inRange(image, lower_bound, upper_bound)
return cv2.countNonZero(mask)
def filter_color_above_box(frame_, box, ignore_colors, height=33, width_ratio=1, tolerance=80): # 颜色忽略
x1, y1, x2, y2 = map(int, box)
box_width = x2 - x1
region_width = int(box_width * width_ratio)
center_x = x1 + box_width // 2
region_x1 = max(0, center_x - region_width // 2)
region_x2 = min(frame_.shape[1], center_x + region_width // 2)
region_above = frame_[max(0, y1 - height):y1, region_x1:region_x2]
if region_above.size == 0:
return False
for ignore_color in ignore_colors:
ignore_color_count = count_pixels_of_color(
region_above, ignore_color, tolerance)
# print(f"忽略颜色 ({ignore_color}) 像素数: {ignore_color_count}")
if ignore_color_count >= 5:
ignore_color_bgr = ignore_color[::-1]
# cv2.rectangle(frame_, (region_x1, max(0, y1-height)), (region_x2, y1), ignore_color_bgr, -1)
# print(f"用颜色填充框上方的区域: {ignore_color}")
return True
# print("盒子经过了彩色滤光片")
return False
def process_aiming(centerx_predicted, centery_predicted, previous_centerx, previous_centery, # 鼠标移动平滑
reverse_threshold_x, reverse_threshold_y, smoothing_factor, lockSpeed,
threshold, slowDownFactor):
"""
处理瞄准逻辑,包括短时间内反向移动的过滤、更新锁定速度和平滑处理。
:param centerx_predicted: 当前预测的X轴瞄准点
:param centery_predicted: 当前预测的Y轴瞄准点
:param previous_centerx: 前一帧的X轴瞄准点
:param previous_centery: 前一帧的Y轴瞄准点
:param reverse_threshold_x: X轴反向移动的阈值
:param reverse_threshold_y: Y轴反向移动的阈值
:param smoothing_factor: 平滑因子(接近1表示更平滑的跟踪)
:param lockSpeed: 当前的锁定速度
:param threshold: 检测目标是否“停止”的速度阈值
:param slowDownFactor: 目标停止时的减速因子
:return: 平滑处理后的瞄准点 (centerx, centery)
"""
global centerx_smoothed, centery_smoothed
# 检测短时间内反向移动,并进行过滤
if abs(centerx_predicted - previous_centerx) < reverse_threshold_x:
centerx_predicted = previous_centerx # 忽略反向移动,保持原方向
if abs(centery_predicted - previous_centery) < reverse_threshold_y:
centery_predicted = previous_centery # 忽略反向移动,保持原方向
# 更新锁定速度
if abs(centerx_predicted) < threshold and abs(centery_predicted) < threshold:
lockSpeed *= slowDownFactor # 目标停止时,减慢速度以精确瞄准
# 指数平滑
centerx_smoothed = (previous_centerx * (1 - smoothing_factor)
) + (centerx_predicted * smoothing_factor)
centery_smoothed = (previous_centery * (1 - smoothing_factor)
) + (centery_predicted * smoothing_factor)
# 使用平滑后的位置和锁定速度来计算鼠标移动距离
centerx = centerx_smoothed * lockSpeed
centery = centery_smoothed * lockSpeed
return centerx, centery
def show_random_offset_window(): # 显示随机瞄准偏移配置窗口
global random_offset_window
random_offset_window.deiconify() # 显示随机瞄准偏移配置窗口
def random_offset_set_window(): # 随机瞄准偏移配置窗口
global random_offset_window, max_offset_entry, min_offset_entry, offset_time_entry
def close_random_offset_window():
random_offset_window.destroy() # 销毁窗口
random_offset_set_window() # 打开并隐藏窗口,方便下次直接显示
load_random_offset() # 加载随机瞄准参数
def load_random_offset(): # 加载随机瞄准参数
global model_file, test_window_frame, screenshot_mode, crawl_information, DXcam_screenshot, dxcam_maxFPS, \
loaded_successfully, stage1_scope, stage1_intensity, stage2_scope, stage2_intensity, segmented_aiming_switch
print('加载随机瞄准参数...')
random_offset_mode_var.set(Opt.get(
"enable_random_offset", False)) # 随机瞄准偏移开关
offset_time_entry.insert(
0, str(Opt.get("time_interval", 1))) # 随即瞄准时间间隔
offset_range = tuple(Opt.get(
"offset_range", [0, 1])) # 随机瞄准偏移范围(0-1)
max_offset_entry.insert(0, str(offset_range[1])) # 插入瞄准偏移最大值
min_offset_entry.insert(0, str(offset_range[0])) # 插入瞄准偏移最小值
print("设置加载成功!")
loaded_successfully = True # 加载成功标识符
def save_random_offset(): # 保存设置
global model_file
print("保存随机瞄准部位参数...")
Opt.update('enable_random_offset', random_offset_mode_var.get())
Opt.update('time_interval', float(offset_time_entry.get()))
Opt.update('offset_range', [
float(min_offset_entry.get()), float(max_offset_entry.get())])
Opt.save()
def save_button():
update_values()
save_random_offset()
random_offset_win_width = 245
random_offset_win_hight = 200
random_offset_window = ctk.CTkToplevel(root)
random_offset_window.title("随机偏移配置")
random_offset_window.geometry(
f"{random_offset_win_width}x{random_offset_win_hight}") # GUI页面大小x
random_offset_window.resizable(False, False) # 禁止窗口大小调整
# 设置当用户点击窗口的关闭按钮时要做的操作
random_offset_window.protocol("WM_DELETE_WINDOW",
close_random_offset_window) # 将WM_DELETE_WINDOW的默认操作设置为 _on_closing 函数
random_offset_window.attributes('-topmost', 1) # 置顶窗口
random_offset_window.withdraw() # 创建后立即隐藏窗口
random_offset_window_frame = ctk.CTkFrame(
random_offset_window, width=random_offset_win_width, height=random_offset_win_hight, fg_color="transparent")
random_offset_window_frame.grid(row=0, column=0, sticky="nsew")
# 最大值输入框
max_offset_lable = ctk.CTkLabel(random_offset_window_frame, width=20, height=0, font=(
"Microsoft YaHei", 16), fg_color="transparent", text="偏移最大值:") # 设置标题文本属性
max_offset_lable.grid(row=1, column=0, sticky="w",
padx=(20, 0), pady=(20, 0)) # 设置标题文本位置属性
max_offset_entry = ctk.CTkEntry(random_offset_window_frame, width=100, font=(
"Microsoft YaHei", 14), fg_color="#8B8989", text_color="black", placeholder_text="请输入(1-0)")
max_offset_entry.grid(row=1, column=0, sticky="n",
padx=(120, 0), pady=(20, 0))
# 最小值输入框
min_offset_lable = ctk.CTkLabel(random_offset_window_frame, width=20, height=0, font=(
"Microsoft YaHei", 16), fg_color="transparent", text="偏移最小值:") # 设置标题文本属性
min_offset_lable.grid(row=2, column=0, sticky="w",
padx=(20, 0), pady=(20, 0)) # 设置标题文本位置属性
min_offset_entry = ctk.CTkEntry(random_offset_window_frame, width=100, font=(
"Microsoft YaHei", 14), fg_color="#8B8989", text_color="black", placeholder_text="请输入(1-0)")
min_offset_entry.grid(row=2, column=0, sticky="n",
padx=(120, 0), pady=(20, 0))
# 间隔时间值输入框
offset_time_lable = ctk.CTkLabel(random_offset_window_frame, width=20, height=0, font=(
"Microsoft YaHei", 16), fg_color="transparent", text="切换间隔(秒):") # 设置标题文本属性
offset_time_lable.grid(row=3, column=0, sticky="w",
padx=(20, 0), pady=(20, 0)) # 设置标题文本位置属性
offset_time_entry = ctk.CTkEntry(random_offset_window_frame, width=100, font=(
"Microsoft YaHei", 14), fg_color="#8B8989", text_color="black", placeholder_text="单位:秒")
offset_time_entry.grid(row=3, column=0, sticky="n",
padx=(120, 0), pady=(20, 0))
# 应用按钮
set_button = ctk.CTkButton(random_offset_window_frame, width=50, image=None,
command=save_button, text="保存并应用", font=("Microsoft YaHei", 16))
set_button.grid(row=5, column=0, sticky="n", padx=(20, 0), pady=(10, 0))
def show_recoil_window(): # 显示辅助压枪配置窗口
global recoil_window
recoil_window.deiconify() # 显示辅助压枪配置窗口
def recoil_set_window(): # 辅助压枪配置窗口
global recoil_window, recoil_interval_entry, recoil_boosted_distance_time_entry, recoil_boosted_distance_entry, recoil_standard_distance_entry, recoil_transition_time_entry
def close_recoil_window():
recoil_window.destroy() # 销毁窗口
recoil_set_window() # 打开并隐藏窗口,方便下次直接显示
load_recoil() # 加载辅助压枪参数
def load_recoil(): # 加载辅助压枪参数
global loaded_successfully, recoil_interval_entry, recoil_boosted_distance_time_entry, recoil_boosted_distance_entry, recoil_standard_distance_entry, recoil_transition_time_entry
print('加载辅助压枪参数...')
recoil_interval_entry.insert(
0, str(Opt.get("recoil_interval", 0.1))) # 压枪间隔
recoil_boosted_distance_entry.insert(
0, str(Opt.get("recoil_boosted_distance", 5))) # 一阶段单次距离
recoil_boosted_distance_time_entry.insert(
0, str(Opt.get("recoil_boosted_distance_time", 0.5))) # 一阶段时间
recoil_standard_distance_entry.insert(
0, str(Opt.get("recoil_standard_distance", 1))) # 二阶段单次距离
recoil_transition_time_entry.insert(
0, str(Opt.get("recoil_transition_time", 0.2))) # 缓冲时间
print("设置加载成功!")
loaded_successfully = True # 加载成功标识符
def save_recoil(): # 保存设置
global model_file
print("保存辅助压枪参数...")
Opt.update('recoil_interval', float(recoil_interval_entry.get()))
Opt.update('recoil_boosted_distance', float(
recoil_boosted_distance_entry.get()))
Opt.update('recoil_boosted_distance_time', float(
recoil_boosted_distance_time_entry.get()))
Opt.update('recoil_standard_distance', float(
recoil_standard_distance_entry.get()))
Opt.update('recoil_transition_time', float(
recoil_transition_time_entry.get()))
Opt.save()
def save_button():
# 检查所有输入的值是否均为数字
entries = [recoil_interval_entry, recoil_boosted_distance_entry, recoil_boosted_distance_time_entry,
recoil_standard_distance_entry, recoil_transition_time_entry]
for entry in entries:
try:
float(entry.get())
except ValueError: # 如果输入的不是数字
tk.messagebox.showerror("输入错误", "所有输入值必须为数字,请重新输入。")
for e in entries:
e.delete(0, 'end') # 清除每个输入框的内容
load_recoil() # 重新加载每个输入框的值
return # 结束函数
# 如果所有输入值均符合要求
update_values()
save_recoil()
recoil_win_width = 460
recoil_win_hight = 220
recoil_window = ctk.CTkToplevel(root)
recoil_window.title("辅助压枪配置")
recoil_window.geometry(
f"{recoil_win_width}x{recoil_win_hight}") # GUI页面大小x
recoil_window.resizable(False, False) # 禁止窗口大小调整
# 设置当用户点击窗口的关闭按钮时要做的操作
recoil_window.protocol("WM_DELETE_WINDOW",
close_recoil_window) # 将WM_DELETE_WINDOW的默认操作设置为 _on_closing 函数
recoil_window.attributes('-topmost', 1) # 置顶窗口
recoil_window.withdraw() # 创建后立即隐藏窗口
recoil_window_frame = ctk.CTkFrame(
recoil_window, width=recoil_win_width, height=recoil_win_hight, fg_color="transparent")
recoil_window_frame.grid(row=0, column=0, sticky="nsew")
# 压枪间隔
recoil_interval_lable = ctk.CTkLabel(recoil_window_frame, width=20, height=0, font=(
"Microsoft YaHei", 16), fg_color="transparent", text="压枪间隔(s):") # 设置标题文本属性
recoil_interval_lable.grid(row=1, column=0, sticky="w", padx=(
20, 0), pady=(20, 0)) # 设置标题文本位置属性
recoil_interval_entry = ctk.CTkEntry(recoil_window_frame, width=100, font=(
"Microsoft YaHei", 14), fg_color="#8B8989", text_color="black", placeholder_text="单位(秒)")
recoil_interval_entry.grid(
row=1, column=0, sticky="n", padx=(120, 0), pady=(20, 0))
# 阶段平滑过度(缓冲时间)
recoil_transition_time_lable = ctk.CTkLabel(recoil_window_frame, width=20, height=0, font=(
"Microsoft YaHei", 16), fg_color="transparent", text="缓冲时间(s):") # 设置标题文本属性
recoil_transition_time_lable.grid(
row=1, column=1, sticky="w", padx=(20, 0), pady=(20, 0)) # 设置标题文本位置属性
recoil_transition_time_entry = ctk.CTkEntry(recoil_window_frame, width=100, font=(
"Microsoft YaHei", 14), fg_color="#8B8989", text_color="black", placeholder_text="单位(秒)")
recoil_transition_time_entry.grid(
row=1, column=1, sticky="n", padx=(120, 0), pady=(20, 0))
# 一阶段力度
recoil_boosted_distance_lable = ctk.CTkLabel(recoil_window_frame, width=20, height=0, font=(
"Microsoft YaHei", 16), fg_color="transparent", text="一阶段力度:") # 设置标题文本属性
recoil_boosted_distance_lable.grid(
row=2, column=0, sticky="w", padx=(20, 0), pady=(20, 0)) # 设置标题文本位置属性
recoil_boosted_distance_entry = ctk.CTkEntry(recoil_window_frame, width=100, font=(
"Microsoft YaHei", 14), fg_color="#8B8989", text_color="black", placeholder_text="单位(像素)")
recoil_boosted_distance_entry.grid(
row=2, column=0, sticky="n", padx=(120, 0), pady=(20, 0))
# 一阶段持续时间
recoil_boosted_distance_time_lable = ctk.CTkLabel(recoil_window_frame, width=20, height=0, font=(
"Microsoft YaHei", 16), fg_color="transparent", text="一阶段时间:") # 设置标题文本属性
recoil_boosted_distance_time_lable.grid(
row=2, column=1, sticky="w", padx=(20, 0), pady=(20, 0)) # 设置标题文本位置属性
recoil_boosted_distance_time_entry = ctk.CTkEntry(recoil_window_frame, width=100, font=(
"Microsoft YaHei", 14), fg_color="#8B8989", text_color="black", placeholder_text="单位:秒")
recoil_boosted_distance_time_entry.grid(
row=2, column=1, sticky="n", padx=(120, 0), pady=(20, 0))
# 二阶段力度
recoil_standard_distance_lable = ctk.CTkLabel(recoil_window_frame, width=20, height=0, font=(
"Microsoft YaHei", 16), fg_color="transparent", text="二阶段力度:") # 设置标题文本属性
recoil_standard_distance_lable.grid(
row=3, column=0, sticky="w", padx=(20, 0), pady=(20, 0)) # 设置标题文本位置属性
recoil_standard_distance_entry = ctk.CTkEntry(recoil_window_frame, width=100, font=(
"Microsoft YaHei", 14), fg_color="#8B8989", text_color="black", placeholder_text="单位(像素)")
recoil_standard_distance_entry.grid(
row=3, column=0, sticky="n", padx=(120, 0), pady=(20, 0))
# 应用按钮
set_button = ctk.CTkButton(recoil_window_frame, width=50, image=None,
command=save_button, text="保存并应用", font=("Microsoft YaHei", 16))
set_button.grid(row=5, column=0, sticky="n", padx=(20, 0), pady=(20, 0))
recoil_set_tips_lable = ctk.CTkLabel(recoil_window_frame, width=20, height=0, font=("Microsoft YaHei", 12),
fg_color="transparent", text="Tips:触发方式与所选自瞄触发方式相同\n"
"力度为每次移动距离,像素点为单位\n"
"缓冲时间为一阶段至二阶段中间的过度时间") # 设置标题文本属性
recoil_set_tips_lable.grid(row=5, column=1, sticky="w", padx=(
0, 0), pady=(10, 0)) # 设置标题文本位置属性
def create_gui_tkinter(): # 软件主题GUI界面
global aimbot_var, lockSpeed_scale, triggerType_var, arduinoMode_var, lockKey_var, confidence_scale, closest_mouse_dist_scale, screen_width_scale, screen_height_scale, root, model_file, model_file_label, aimOffset_scale, draw_center_var, mouse_Side_Button_Witch_var, LookSpeed_label_text, lockSpeed_variable, confidence_variable, closest_mouse_dist_variable, aimOffset_variable, screen_width_scale_variable, screen_height_scale_variable, image_label, image_label_switch, image_label_FPSlabel, target_selection_var, target_mapping, prediction_factor_variable, prediction_factor_scale, method_of_prediction_var, extra_offset_x_scale, extra_offset_y_scale, extra_offset_y, extra_offset_x, extra_offset_x_variable, extra_offset_y_variable, readme_content, screenshot_mode_var, screenshot_mode, segmented_aiming_switch_var, stage1_scope, stage1_scope_scale, stage1_scope_variable, stage1_intensity_variable, stage1_intensity, stage1_intensity_scale, stage2_scope_variable, stage2_intensity_variable, stage2_scope_scale, stage2_intensity_scale, aimOffset_x, aimOffset_variable_x, aimOffset_x_scale, mouseMove_var, random_offset_mode_var, random_offset_mode_check, recoil_check, recoil_var, tolerance_variable, tolerance_scale, ignore_colors_entry, ignore_colors_variable, automatic_Trigger_var, mouse_movement_smoothing_switch, smooth_aiming_var, predict_model_var
# 版本号