文章目录
- 前言
- GUI功能整合
- 提示框
- 功能整合
- 总体代码
- 自定义线程池
- YoloDectect
- 工具类
- 窗口绘制
- 鼠标控制
- 控制器
- GUI界面
- 总结
前言
okey,大概经过,两天的开发,我在这里完成了基本的全部开发。
那么我们先来看看大概的效果吧:
在这里的话,全部功能是做好了的。当然还有很多需要优化的地方。
GUI功能整合
先前,我们做好了控制,然后还有这个绘图等等,包括我们算法的整合。
那么现在的话,就是把功能到这里的整合。
这里的话我们,就直接看到代码了。
提示框
还是我们的提示框,这个改了一点样式啥的。
class ToolTip:
def __init__(self, widget, text):
self.widget = widget
self.tooltip = None
self.text = text
self.widget.bind("<Enter>", self.show_tooltip)
self.widget.bind("<Leave>", self.hide_tooltip)
def show_tooltip(self, event=None):
x, y, _, _ = self.widget.bbox("insert")
x += self.widget.winfo_rootx() + 25
y += self.widget.winfo_rooty() + 25
self.tooltip = tk.Toplevel(self.widget)
self.tooltip.wm_overrideredirect(True)
self.tooltip.wm_geometry(f"+{x}+{y}")
label = ttk.Label(self.tooltip, text=self.text, background="#00BFFF", relief="solid", borderwidth=1)
label.pack()
def hide_tooltip(self, event=None):
if self.tooltip:
self.tooltip.destroy()
self.tooltip = None
功能整合
之后就是我们的功能整合。这里的话,很多东西在Mycontroller里面封装好了。直接做功能就好了。
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_close)
self.title("YOLO辅助瞄准系统")
self.geometry("300x300") # 设置固定的窗口大小
self.resizable(False, False) # 禁止调整窗口大小
self.style = ttk.Style(self)
self.style.configure("TButton",
padding=6,
relief="flat",
background="#0078d7",
foreground="white",
font=("Arial", 12, "bold"))
self.style.configure("TCheckbutton",
padding=4,
font=("Arial", 12))
self.create_widgets()
self.controller = MyController()
self.program_pausing = False
def create_widgets(self):
self.location_assist_var = tk.BooleanVar(value=True)
self.location_assist_checkbox = ttk.Checkbutton(self,
text="枪口定位",
variable=self.location_assist_var,
command=self.check_gan
)
self.location_assist_checkbox.pack()
ToolTip(self.location_assist_checkbox, "开启后按住鼠标左键,定位枪口位置,按F12关闭,"
"如果需要控制其他软件,请先关闭!!!")
self.draw_box_var = tk.BooleanVar(value=True)
self.draw_box_checkbox = ttk.Checkbutton(self, text="绘制框图",
variable=self.draw_box_var,
command=self.check_draw
)
self.draw_box_checkbox.pack()
ToolTip(self.draw_box_checkbox, "绘制算法识别到的目标")
self.algorithm_detection_var = tk.BooleanVar(value=True)
self.algorithm_detection_checkbox = ttk.Checkbutton(self, text="开启算法",
variable=self.algorithm_detection_var,
command=self.check_alg
)
self.algorithm_detection_checkbox.pack()
ToolTip(self.algorithm_detection_checkbox, "开启Yolo算法进行识别")
self.aim_assist_var = tk.BooleanVar(value=True)
self.aim_assist_checkbox = ttk.Checkbutton(self, text="辅助瞄准",
variable=self.aim_assist_var,
command=self.check_track
)
self.aim_assist_checkbox.pack()
ToolTip(self.aim_assist_checkbox, "基于算法进行定位,实时定位目标")
self.start_button = ttk.Button(self, text="开启", command=self.start_program)
self.start_button.pack(pady=10)
ToolTip(self.start_button, "请进入游戏后开启所有功能")
self.pause_button = ttk.Button(self, text="挂起", command=self.pause_program)
self.pause_button.pack(pady=10)
ToolTip(self.pause_button, "为避免按键冲突,在离开游戏后,点击挂起,注意,请先关闭枪口定位")
self.quit_button = ttk.Button(self, text="停止", command=self.quit_program)
self.quit_button.pack(pady=10)
ToolTip(self.quit_button, "停止当前辅助程序,窗口不关闭,请先停止当前辅助后关闭程序")
def check_gan(self):
if(self.location_assist_var.get()):
self.controller.check_open_gan_gui()
else:
self.controller.check_stop_gan_gui()
def check_draw(self):
if(self.draw_box_var.get()):
self.controller.check_open_draw_gui()
else:
self.controller.check_stop_draw_gui()
def check_alg(self):
if(self.algorithm_detection_var.get()):
self.controller.check_open_alg_gui()
else:
self.controller.check_stop_alg_gui()
def check_track(self):
if(self.aim_assist_var.get()):
self.controller.check_open_track_gui()
else:
self.controller.check_stop_track_gui()
def start_program(self):
if(self.start_button["text"] == "已开启"):
messagebox.showwarning("警告", "辅助系统已启动,若要重启,请先停止")
else:
messagebox.showinfo("提示","欢迎使用Yolo AI辅助系统,Huterox is Awesome!")
self.start_button["text"] = "正在开启..."
self.controller.start()
self.start_button["text"] = "已开启"
if(self.quit_button["text"] == "已关闭"):
self.quit_button["text"] = "停止"
def pause_program(self):
#没有挂起进行挂起
if(self.start_button["text"]=="开启"):
messagebox.showwarning("警告","还未开启,无需挂起")
return
if(not self.program_pausing):
self.pause_button["text"] = "挂起中..."
self.controller.start_pause()
self.pause_button["text"] = "已挂起"
self.program_pausing = True
else:
self.pause_button["text"] = "恢复中..."
self.controller.stop_pause()
self.pause_button["text"] = "挂起"
self.program_pausing = False
def quit_program(self):
if(self.start_button["text"] == "开启"):
messagebox.showwarning("警告","请先启动")
else:
self.controller.stop_all()
self.quit_button["text"] = "已关闭"
self.start_button["text"] = "开启"
def on_close(self):
if(self.quit_button["text"]=="停止" and self.start_button["text"]=="已开启"):
messagebox.showwarning("警告","请先停止")
else:
self.destroy()
总体代码
okey,最后来看看我们每个部分的代码,因为到现在,很多代码都修改过。
自定义线程池
这个玩意辅助帮助我们快速启动一个线程。
class ThreadPoolManager:
def __init__(self, max_workers=5, idle_timeout=60):
self.max_workers = max_workers
self.idle_timeout = idle_timeout
self.executor = concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers)
self.worker_count = 0
def execute(self, task, *args, **kwargs):
# 提交任务给线程池
future = self.executor.submit(task, *args, **kwargs)
# 更新工作线程数量
with concurrent.futures.ThreadPoolExecutor() as temp_executor:
self.worker_count = temp_executor._adjust_thread_count()
return future
def get_result(self, future):
return future.result()
def shutdown(self):
# 关闭线程池
self.executor.shutdown()
def _adjust_thread_count(self):
# 自动调整线程数量
if self.worker_count < self.max_workers and self.executor._idle_semaphore.acquire(timeout=0):
# 创建新的工作线程
self.worker_count += 1
return True
elif self.worker_count > 1 and self.executor._idle_semaphore.release():
# 销毁多余的空闲线程
self.worker_count -= 1
return True
else:
return False
YoloDectect
之后就是我们的算法识别器了。这里注意的是我这里要的是onnx模型。当然还是yolov5-lite改过来的。怎么制作数据集训练啥的,我都有写过,或者你自己去别的地方看都可以。
class YoloDectect():
def __init__(self,
model_pb_path=r'F:\projects\PythonProject\YOLOv5-Lite-master\weights\v5lite-s.onnx',
label_path='coco.names',
confThreshold=0.6,
nmsThreshold=0.3,
objThreshold=0.6):
so = ort.SessionOptions()
so.log_severity_level = 3
self.net = ort.InferenceSession(model_pb_path, so)
self.classes = list(map(lambda x: x.strip(), open(label_path, 'r').readlines()))
self.num_classes = len(self.classes)
"""
数据集聚类得到的anchors的参数
"""
anchors = [[10, 13, 16, 30, 33, 23],
[30, 61, 62, 45, 59, 119],
[116, 90, 156, 198, 373, 326]
]
self.nl = len(anchors)
self.na = len(anchors[0]) // 2
self.no = self.num_classes + 5
self.grid = [np.zeros(1)] * self.nl
self.stride = np.array([8., 16., 32.])
self.anchor_grid = np.asarray(anchors, dtype=np.float32).reshape(self.nl, -1, 2)
self.confThreshold = confThreshold
self.nmsThreshold = nmsThreshold
self.objThreshold = objThreshold
self.input_shape = (self.net.get_inputs()[0].shape[2], self.net.get_inputs()[0].shape[3])
def resize_image(self, srcimg, keep_ratio=True):
"""
修改图像尺寸为目标网络尺寸
:param srcimg:
:param keep_ratio:
:return:
"""
top, left, newh, neww = 0, 0, self.input_shape[0], self.input_shape[1]
if keep_ratio and srcimg.shape[0] != srcimg.shape[1]:
hw_scale = srcimg.shape[0] / srcimg.shape[1]
if hw_scale > 1:
newh, neww = self.input_shape[0], int(self.input_shape[1] / hw_scale)
img = cv2.resize(srcimg, (neww, newh), interpolation=cv2.INTER_AREA)
left = int((self.input_shape[1] - neww) * 0.5)
img = cv2.copyMakeBorder(img, 0, 0, left, self.input_shape[1] - neww - left, cv2.BORDER_CONSTANT,
value=0) # add border
else:
newh, neww = int(self.input_shape[0] * hw_scale), self.input_shape[1]
img = cv2.resize(srcimg, (neww, newh), interpolation=cv2.INTER_AREA)
top = int((self.input_shape[0] - newh) * 0.5)
img = cv2.copyMakeBorder(img, top, self.input_shape[0] - newh - top, 0, 0, cv2.BORDER_CONSTANT, value=0)
else:
img = cv2.resize(srcimg, self.input_shape, interpolation=cv2.INTER_AREA)
return img, newh, neww, top, left
def _make_grid(self, nx=20, ny=20):
xv, yv = np.meshgrid(np.arange(ny), np.arange(nx))
return np.stack((xv, yv), 2).reshape((-1, 2)).astype(np.float32)
def postprocess(self, frame, outs, pad_hw):
"""
完成目标识别和NMS,并且得到目标的左上角坐标和宽高,
:param frame:
:param outs:
:param pad_hw:
:return: results = [{'box':box,'cls':cls,'conf':conf,'id':id}]
"""
newh, neww, padh, padw = pad_hw
frameHeight = frame.shape[0]
frameWidth = frame.shape[1]
ratioh, ratiow = frameHeight / newh, frameWidth / neww
classIds = []
confidences = []
box_index = []
boxes = []
outs = outs[outs[:, 4] > self.objThreshold]
for detection in outs:
scores = detection[5:]
classId = np.argmax(scores)
confidence = scores[classId]
if confidence > self.confThreshold: # and detection[4] > self.objThreshold:
center_x = int((detection[0] - padw) * ratiow)
center_y = int((detection[1] - padh) * ratioh)
width = int(detection[2] * ratiow)
height = int(detection[3] * ratioh)
left = int(center_x - width / 2)
top = int(center_y - height / 2)
classIds.append(classId)
confidences.append(float(confidence))
boxes.append([left, top, width, height])
indices = cv2.dnn.NMSBoxes(boxes, confidences, self.confThreshold, self.nmsThreshold)
for ix in indices:
box_index.append(ix)
result = []
for i in box_index:
box = boxes[i]
cls = self.classes[classIds[i]]
conf = confidences[i]
item = {'box':box,'cls':cls,'conf':conf,'id':classIds[i]}
result.append(item)
return result
def detect(self, srcimg):
img, newh, neww, top, left = self.resize_image(srcimg)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = img.astype(np.float32) / 255.0
blob = np.expand_dims(np.transpose(img, (2, 0, 1)), axis=0)
outs = self.net.run(None, {self.net.get_inputs()[0].name: blob})[0].squeeze(axis=0)
row_ind = 0
for i in range(self.nl):
h, w = int(self.input_shape[0] / self.stride[i]), int(self.input_shape[1] / self.stride[i])
length = int(self.na * h * w)
if self.grid[i].shape[2:4] != (h, w):
self.grid[i] = self._make_grid(w, h)
outs[row_ind:row_ind + length, 0:2] = (outs[row_ind:row_ind + length, 0:2] * 2. - 0.5 + np.tile(
self.grid[i], (self.na, 1))) * int(self.stride[i])
outs[row_ind:row_ind + length, 2:4] = (outs[row_ind:row_ind + length, 2:4] * 2) ** 2 * np.repeat(
self.anchor_grid[i], h * w, axis=0)
row_ind += length
results = self.postprocess(srcimg, outs, (newh, neww, top, left))
return results
工具类
之后的话,是我们的工具类。
class ScreenUtils():
@staticmethod
def fitiler(item):
#对某些item进行过滤
if(item['cls']=="person"):
return True
else:
return False
@staticmethod
def get_real_resolution():
"""获取真实的分辨率"""
hDC = win32gui.GetDC(0)
# 横向分辨率
w = win32print.GetDeviceCaps(hDC, win32con.DESKTOPHORZRES)
# 纵向分辨率
h = win32print.GetDeviceCaps(hDC, win32con.DESKTOPVERTRES)
return w, h
@staticmethod
def get_screen_size():
"""获取缩放后的分辨率"""
w = GetSystemMetrics (0)
h = GetSystemMetrics (1)
return w, h
@staticmethod
def scale_rate():
real_resolution = ScreenUtils.get_real_resolution()
screen_size = ScreenUtils.get_screen_size()
screen_scale_rate = round(real_resolution[0] / screen_size[0], 2)
return screen_scale_rate
其实这个玩意没啥,就是屏幕分辨率啥的获取,然后是一个过滤器,这个后面用来过滤掉要跟踪显示的目标框的,比如你要瞄准头部,那么就留下头部的。这个看数据集自己看着改。
窗口绘制
这里还是有点bug,就是绘制的目标框闪动的问题,目前我没有找到合适的方案。估计是哪里还有问题,这个我懒得调了,没必要了。
class RectangleDrawer:
def __init__(self,size=100,pool_size=6):
self.draw_helper_pool = ThreadPoolManager(max_workers=pool_size)
self.size = size
self.screen_scale_rate = ScreenUtils.scale_rate()
self.dc = win32gui.GetDC(0)
self.dcObj = win32ui.CreateDCFromHandle(self.dc)
self.hwnd = win32gui.WindowFromPoint((0, 0))
self.monitor = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1))
self.red = win32api.RGB(255, 0, 0) # Red
self.drawing = False
# rgbs = np.random.rand(32, 3) * 255
rgbs = [(255,0,0),(255,255,0),(0,0,204),(0,255,0)]
self.screen_width = win32api.GetSystemMetrics(0)
self.screen_height = win32api.GetSystemMetrics(1)
self.colours = [win32api.RGB(int(c[0]), int(c[1]), int(c[2])) for c in rgbs]
self.going_draw = True
self.items = []
self.draw_recgs = True
def drawRectanglesBySelf(self,fps=60):
def go():
t = 1/fps
while(self.going_draw):
time.sleep(t)
self.drawRectangles()
self.draw_helper_pool.execute(go)
def setItems(self,items):
self.items = items
def drawRectangles(self):
"""
绘制多个目标框
:param items:
:return:
"""
if(not self.draw_recgs):
return
for item in self.items:
text = item['cls']+":"+"conf:"+"{:.2f}".format(item['conf'])
left,top,width,height = item['box']
color = self.colours[int(item['id']%len(self.colours))]
# 进行坐标边界检查
left = max(0, min(left, self.screen_width - 1))
top = max(0, min(top, self.screen_height - 1))
right = max(0, min(left + width, self.screen_width - 1))
bottom = max(0, min(top + height, self.screen_height - 1))
# 绘制矩形框
"""
这里也是,现在这边测试用的是coco数据集,后面改成自己的,那么这里需要进行
一定的修改,当然,这里的话,主要在这个ScreenUtils.fitiler里面去修改
"""
self.new_items = True
if(ScreenUtils.fitiler(item)):
# self.draw_helper_pool.execute(self.drawSingle,text, left, top, right - left, bottom - top, color)
self.drawSingle(text, left, top, right - left, bottom - top, color)
def drawSingle(self,text,left,top,width,height,color,h=5):
start_x = int(left)
start_y = int(top)
# past_coordinates = self.monitor
past_coordinates = (start_x - 2 * width, start_y - 2 * height,
start_x + 2 * width, start_y + 2 * height
)
rect = win32gui.CreateRoundRectRgn(*past_coordinates, 2, 2)
win32gui.RedrawWindow(self.hwnd, past_coordinates, rect, win32con.RDW_INVALIDATE)
try:
for k in range(h):
#绘制多重框
for x in range(width-k):
#绘制两条横线
win32gui.SetPixel(self.dc, start_x + x, start_y+k, color)
win32gui.SetPixel(self.dc, start_x + x, start_y + height-k, color)
for y in range(height-k):
#绘制两条竖线
win32gui.SetPixel(self.dc, start_x + k, start_y + y + k, color)
win32gui.SetPixel(self.dc, start_x + width-k, start_y + y + k, color)
text_coordinates = (
start_x -width, start_y - height,
start_x + 2 * width, start_y + height
)
# 在矩形框中显示文字
win32gui.DrawText(self.dc, text, -1, text_coordinates,
win32con.DT_CENTER | win32con.DT_VCENTER | win32con.DT_SINGLELINE)
except Exception as e:
pass
def draw(self,text="你好"):
past_coordinates = self.monitor
while(self.drawing):
m = win32gui.GetCursorPos()
rect = win32gui.CreateRoundRectRgn(*past_coordinates, 2, 2)
win32gui.RedrawWindow(self.hwnd, past_coordinates, rect, win32con.RDW_INVALIDATE)
start_x = int(m[0]*self.screen_scale_rate)
start_y = int(m[1]*self.screen_scale_rate)
for x in range(self.size):
win32gui.SetPixel(self.dc, start_x + x, start_y, self.red)
win32gui.SetPixel(self.dc, start_x + x, start_y + self.size, self.red)
win32gui.SetPixel(self.dc, start_x, start_y + x, self.red)
win32gui.SetPixel(self.dc, start_x + self.size, start_y + x, self.red)
past_coordinates = (start_x - 2*self.size, start_y - 2*self.size, start_x + 2*self.size, start_y + 2*self.size)
text_coordinates = (
start_x -self.size, start_y - self.size, start_x + 2 * self.size, start_y + self.size)
# 在矩形框中显示文字
win32gui.DrawText(self.dc, text, -1, text_coordinates,
win32con.DT_CENTER | win32con.DT_VCENTER | win32con.DT_SINGLELINE)
鼠标控制
然后是鼠标控制,跟踪啥的都在这里。这里跟踪就没有必要用啥SORT算法了,这里很简单,你要自瞄,肯定是要瞄准你的鼠标准心离得最近的目标呀,是吧,只要排个序就好了。
class MoveMouse():
"""
负责实现鼠标的控制,
注意这里是按照百分之100来算的,实际的一定位置需要乘以
self.screen_scale_rate = ScreenUtils.scale_rate()
"""
def __init__(self,pool_size=3):
self.stop_stay_location_flag = False
self.draw_helper_pool = ThreadPoolManager(max_workers=pool_size)
self.stop_position = []
self.listener_started = False
self.listener = None
self.listener_right = None
self.listener_right_mu = True
self.listener_btn = None
self.listener_btn_mu = True
self.listener_left_mu = True
self.tracking_open = False
self.tracking_stop = False
self.items = []
self.listener_right_mu = True
self.star_listener_flag = True
self.star_listener_right_flag = True
self.star_listener_btn_flag = True
def move_mouse(self,x,y,duration,scale):
x = int(x*scale)
y = int(y*scale)
pydirectinput.moveTo(x, y, duration=duration)
def stop_stay_location(self):
self.stop_position = []
self.stop_stay_location_flag = True
def move_stay_location(self,duration=0.2):
if (len(self.stop_position) == 0):
self.stop_position = pydirectinput.position()
else:
return
self.stop_stay_location_flag = False
y = self.stop_position[1]
x = self.stop_position[0]
def stay_aways():
while(not self.stop_stay_location_flag):
self.move_mouse(x, y, duration,1)
self.draw_helper_pool.execute(stay_aways)
#开启枪口定位
def __func_stay_gan(self,x, y, button, pressed):
if button == Button.left and pressed and self.listener_left_mu:
self.stop_stay_location_flag = False
self.move_stay_location(0.05)
elif button == Button.left and not pressed and self.listener_left_mu:
self.stop_stay_location()
#开启右键跟随监控,这是异步的
def __func_stay_track(self,x, y, button, pressed):
if button == Button.right and pressed and self.listener_right_mu:
self.tracking_open = True
self.func_track_ing()
elif button == Button.right and not pressed and self.listener_right_mu:
self.tracking_open = False
#停止枪口定位的,因为很多功能和鼠标左键绑定,需要进行操作的时候,就需要处理好这个
def __func_stop_gan(self, key):
# 检测是否按下 F12 键
if key == keyboard.Key.f12 and self.listener_btn_mu:
self.listener_left_mu=False
def __start_listener(self):
while (self.star_listener_flag):
if(not self.listener):
self.listener = Listener(on_click=self.__func_stay_gan)
else:
self.listener.start()
# time.sleep(0.1)
def __start_listener_right(self):
while (self.star_listener_right_flag):
if(not self.listener_right):
self.listener_right = Listener(on_click=self.__func_stay_track)
else:
self.listener_right.start()
# time.sleep(0.1)
def __start_listener_btn(self):
while (self.star_listener_btn_flag):
if(not self.listener_btn):
self.listener_btn = keyboard.Listener(on_press=self.__func_stop_gan)
else:
self.listener_btn.start()
# time.sleep(0.1)
def func_run_mouse(self):
"""
启动鼠标部分的功能
:return:
"""
if(self.listener_started):
return
self.listener_btn_mu = True
self.listener_left_mu = True
self.listener_right_mu = True
self.star_listener_flag = True
self.star_listener_right_flag = True
self.star_listener_btn_flag = True
#这两个是按下鼠标之后才可以启动的
# self.tracking_open = True
# self.stop_stay_location_flag = False
self.listener_started = True
self.draw_helper_pool.execute(self.__start_listener)
self.draw_helper_pool.execute(self.__start_listener_right)
self.draw_helper_pool.execute(self.__start_listener_btn)
def func_stop_mouse(self):
"""
销毁鼠标的功能,包括,停止鼠标跟踪,关闭现在正在执行的相关线程
:return:
"""
self.star_listener_flag = False
self.star_listener_right_flag = False
self.star_listener_btn_flag = False
self.listener_btn_mu = False
self.listener_left_mu = False
self.listener_right_mu = False
#这两个加上就是理解停止
self.tracking_open = False
self.stop_stay_location_flag = True
self.listener.stop()
self.listener_right.stop()
self.listener_btn.stop()
def setItems(self,items):
self.items = items
def func_track_ing(self):
"""
计算得到离得最近的目标,然后进行跟踪,这个也是异步的
设置self.tracking_open=False结束线程,结束跟踪
这里的话,后面还可以过滤一下,辅助瞄准头部,还是身体,目前这边是跟踪
离当前鼠标位置最近的。
"""
def tracking():
while(self.tracking_open):
# 这个是100%来得到的,items里面的是125%得到的,因为它是直接截屏得到的
x, y = pydirectinput.position()
target_x, target_y = x, y
dist = float('inf')
flag_t = False
for it in self.items:
if(ScreenUtils.fitiler(it)):
centerx, centery = (it['box'][0] + it['box'][0] + it['box'][2]) // 2, (
it['box'][1] + it['box'][1] + it['box'][3]) // 2
c_dist = ((centerx - x) ** 2 + (centery - y) ** 2) ** 0.5
if (c_dist < dist):
dist = c_dist
target_x, target_y = centerx, centery
flag_t = True
if(flag_t):
self.move_mouse(target_x,target_y,0.5,1)
self.draw_helper_pool.execute(tracking)
控制器
然后是控制器,这个没啥了。
class MyController:
"""
负责获全局控制。
"""
def __init__(self):
self.pools = ThreadPoolManager(max_workers=3)
self.net = YoloDectect()
self.drawer = RectangleDrawer()
self.go = True
self.move_mouse = MoveMouse()
self.drawer_flag = True
self.alg_open = True
def check_open_gan_gui(self):
"""
为GUI提供开启枪口定位的功能
:return:
"""
self.move_mouse.listener_left_mu = True
def check_stop_gan_gui(self):
self.move_mouse.listener_left_mu = False
def check_open_draw_gui(self):
self.drawer_flag = True
def check_stop_draw_gui(self):
self.drawer_flag = False
def check_open_alg_gui(self):
self.alg_open = True
def check_stop_alg_gui(self):
self.alg_open = False
def check_open_track_gui(self):
self.move_mouse.listener_right_mu = True
def check_stop_track_gui(self):
self.move_mouse.listener_right_mu = False
def start(self):
#全部功能
#1. 开启鼠标控制的功能
self.move_mouse.func_run_mouse()
#2. 开启算法和绘制图像的功能
self.drawer_flag = True
self.alg_open = True
self.go = True
self.move_mouse.listener_started = False
# listener_thread = threading.Thread(target=self.runing,args=(10,60,))
# listener_thread.start()
self.pools.execute(self.runing,10,70)
def runing(self,fps,draw_fps):
"""
:param fps: 扫描帧率,这个不需要太高,差不多就可以
:return:
"""
#这里的fps是指,绘制fps,这个可以高一点
w, h = ScreenUtils.get_real_resolution()
self.monitor = {"top": 0, "left": 0, "width": w, "height": h}
self.mss_obj = mss.mss()
self.drawer.drawRectanglesBySelf(fps=draw_fps)
need_p_time = 1/fps
while self.go:
start_time = time.time()
#开启算法
if(self.alg_open):
# 获取屏幕截图
screenshot = self.mss_obj.grab(self.monitor)
# 将截图转换为OpenCV格式
screenshot_cv = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
results = self.net.detect(screenshot_cv)
self.drawer.setItems(results)
self.move_mouse.setItems(results)
else:
self.drawer.setItems([])
self.move_mouse.setItems([])
#开启绘图
if(self.drawer_flag):
self.drawer.draw_recgs = True
else:
self.drawer.draw_recgs = False
# self.drawer.drawRectangles()
end_time = time.time()
real_time = end_time-start_time
dt = need_p_time - real_time
if(dt>0):
time.sleep(dt)
def start_pause(self):
#挂起,修改一下标志位就好了,先不要停止
self.move_mouse.listener_right_mu = False
self.move_mouse.listener_left_mu = False
self.move_mouse.listener_btn_mu = False
self.drawer_flag = False
self.alg_open = False
def stop_pause(self):
#恢复
self.move_mouse.listener_right_mu = True
self.move_mouse.listener_left_mu = True
self.move_mouse.listener_btn_mu = True
self.drawer_flag = True
self.alg_open = True
def stop_all(self):
#所有的算法程序
self.move_mouse.func_stop_mouse()
self.go = False
self.drawer_flag = False
self.alg_open = False
self.move_mouse.listener_started = True
GUI界面
ok,到了最后的GUI模块
#定义GUI界面
class ToolTip:
def __init__(self, widget, text):
self.widget = widget
self.tooltip = None
self.text = text
self.widget.bind("<Enter>", self.show_tooltip)
self.widget.bind("<Leave>", self.hide_tooltip)
def show_tooltip(self, event=None):
x, y, _, _ = self.widget.bbox("insert")
x += self.widget.winfo_rootx() + 25
y += self.widget.winfo_rooty() + 25
self.tooltip = tk.Toplevel(self.widget)
self.tooltip.wm_overrideredirect(True)
self.tooltip.wm_geometry(f"+{x}+{y}")
label = ttk.Label(self.tooltip, text=self.text, background="#00BFFF", relief="solid", borderwidth=1)
label.pack()
def hide_tooltip(self, event=None):
if self.tooltip:
self.tooltip.destroy()
self.tooltip = None
class Application(tk.Tk):
def __init__(self):
super().__init__()
self.protocol("WM_DELETE_WINDOW", self.on_close)
self.title("YOLO辅助瞄准系统")
self.geometry("300x300") # 设置固定的窗口大小
self.resizable(False, False) # 禁止调整窗口大小
self.style = ttk.Style(self)
self.style.configure("TButton",
padding=6,
relief="flat",
background="#0078d7",
foreground="white",
font=("Arial", 12, "bold"))
self.style.configure("TCheckbutton",
padding=4,
font=("Arial", 12))
self.create_widgets()
self.controller = MyController()
self.program_pausing = False
def create_widgets(self):
self.location_assist_var = tk.BooleanVar(value=True)
self.location_assist_checkbox = ttk.Checkbutton(self,
text="枪口定位",
variable=self.location_assist_var,
command=self.check_gan
)
self.location_assist_checkbox.pack()
ToolTip(self.location_assist_checkbox, "开启后按住鼠标左键,定位枪口位置,按F12关闭,"
"如果需要控制其他软件,请先关闭!!!")
self.draw_box_var = tk.BooleanVar(value=True)
self.draw_box_checkbox = ttk.Checkbutton(self, text="绘制框图",
variable=self.draw_box_var,
command=self.check_draw
)
self.draw_box_checkbox.pack()
ToolTip(self.draw_box_checkbox, "绘制算法识别到的目标")
self.algorithm_detection_var = tk.BooleanVar(value=True)
self.algorithm_detection_checkbox = ttk.Checkbutton(self, text="开启算法",
variable=self.algorithm_detection_var,
command=self.check_alg
)
self.algorithm_detection_checkbox.pack()
ToolTip(self.algorithm_detection_checkbox, "开启Yolo算法进行识别")
self.aim_assist_var = tk.BooleanVar(value=True)
self.aim_assist_checkbox = ttk.Checkbutton(self, text="辅助瞄准",
variable=self.aim_assist_var,
command=self.check_track
)
self.aim_assist_checkbox.pack()
ToolTip(self.aim_assist_checkbox, "基于算法进行定位,实时定位目标")
self.start_button = ttk.Button(self, text="开启", command=self.start_program)
self.start_button.pack(pady=10)
ToolTip(self.start_button, "请进入游戏后开启所有功能")
self.pause_button = ttk.Button(self, text="挂起", command=self.pause_program)
self.pause_button.pack(pady=10)
ToolTip(self.pause_button, "为避免按键冲突,在离开游戏后,点击挂起,注意,请先关闭枪口定位")
self.quit_button = ttk.Button(self, text="停止", command=self.quit_program)
self.quit_button.pack(pady=10)
ToolTip(self.quit_button, "停止当前辅助程序,窗口不关闭,请先停止当前辅助后关闭程序")
def check_gan(self):
if(self.location_assist_var.get()):
self.controller.check_open_gan_gui()
else:
self.controller.check_stop_gan_gui()
def check_draw(self):
if(self.draw_box_var.get()):
self.controller.check_open_draw_gui()
else:
self.controller.check_stop_draw_gui()
def check_alg(self):
if(self.algorithm_detection_var.get()):
self.controller.check_open_alg_gui()
else:
self.controller.check_stop_alg_gui()
def check_track(self):
if(self.aim_assist_var.get()):
self.controller.check_open_track_gui()
else:
self.controller.check_stop_track_gui()
def start_program(self):
if(self.start_button["text"] == "已开启"):
messagebox.showwarning("警告", "辅助系统已启动,若要重启,请先停止")
else:
messagebox.showinfo("提示","欢迎使用Yolo AI辅助系统,Huterox is Awesome!")
self.start_button["text"] = "正在开启..."
self.controller.start()
self.start_button["text"] = "已开启"
if(self.quit_button["text"] == "已关闭"):
self.quit_button["text"] = "停止"
def pause_program(self):
#没有挂起进行挂起
if(self.start_button["text"]=="开启"):
messagebox.showwarning("警告","还未开启,无需挂起")
return
if(not self.program_pausing):
self.pause_button["text"] = "挂起中..."
self.controller.start_pause()
self.pause_button["text"] = "已挂起"
self.program_pausing = True
else:
self.pause_button["text"] = "恢复中..."
self.controller.stop_pause()
self.pause_button["text"] = "挂起"
self.program_pausing = False
def quit_program(self):
if(self.start_button["text"] == "开启"):
messagebox.showwarning("警告","请先启动")
else:
self.controller.stop_all()
self.quit_button["text"] = "已关闭"
self.start_button["text"] = "开启"
def on_close(self):
if(self.quit_button["text"]=="停止" and self.start_button["text"]=="已开启"):
messagebox.showwarning("警告","请先停止")
else:
self.destroy()
if __name__ == '__main__':
app = Application()
app.mainloop()
总结
以上就是全部内容了,其实不难,当然这里还有很多问题,一个是驱动问题,由于游戏的一些机制的问题,可以做到鼠标固定,但还是无法压枪,当然这个可能是当前这个游戏的问题。然后是目标识别算法的问题,要求精度要高,尤其和CF,csgo不一样,这种大场景的游戏,它场景复杂一点,需要更多的数据处理,来训练到模型。当然最直接的方案还是直接 抓 内存,然后C++一条龙走起。唉,突然羡慕那些系统工程师了,越来越觉得web没啥意思了。