AI辅助瞄准系统开发与实战(三)-竣工

news2025/2/4 8:47:18

文章目录

  • 前言
  • 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没啥意思了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/761944.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3通透教程【十八】TS为组件的props标注类型

文章目录 &#x1f31f; 写在前面&#x1f31f; 回顾defineProps的基础写法&#x1f31f; defineProps的TS写法&#x1f31f; withDefaults方法&#x1f31f; 拓展&#x1f31f; 写在最后 &#x1f31f; 写在前面 专栏介绍&#xff1a; 凉哥作为 Vue 的忠实 粉丝输出过大量的 …

解决 Error:java: Compilation failed: internal java compiler error

编译失败 因为maven工程项目的 多个model 模块的jdk版本不同 改成一样的就可以了

mysql进阶 —— 主从复制和读写分离

前言 在这篇文章中荔枝会梳理MySQL中有关主从复制和读写分离的相关知识点&#xff0c;主要包括基本的概念、配置搭建、命令和模式选择等几个方面。MySQL主从复制和读写分离属于MySQL数据库学习中的高阶内容了&#xff0c;大家要和荔枝一起加油学习噢~~~希望能帮助到需要的小伙伴…

【机密计算标准】GB/T 41388-2022 可信执行环境基础安全规范

1 范围 本文件确立了可信执行环境系统整体技术架构&#xff0c;描述了可信执行环境基础要求、可信虚拟化系统、可信操作系统、可信应用与服务管理、跨平台应用中间件等主要内容及其测试评价方法。 2 规范性引用文件 下列文件中的内容通过文中的规范性引用面构成本文件必不…

MFC 基于数据库的管理系统

文章目录 初始化设置菜单 添加数据库类创建数据库配置数据库 全部代码 初始化 创建文件选择基于CListView 初始化数据 public:CListCtrl& m_list;CSQLView::CSQLView() noexcept:m_list(GetListCtrl()) {// TODO: 在此处添加构造代码}void CSQLView::OnInitialUpdate() {C…

RK3588 CPU GPU NPU 定频和性能模式设置方法以及温度监控

一. CPU定频 1. RK3588的cpu是4个A554个A76&#xff0c;分为3组单独管理&#xff0c;节点分别是&#xff1a; /sys/devices/system/cpu/cpufreq/policy0&#xff08;对应四个A55:CPU0-CPU3&#xff09; /sys/devices/system/cpu/cpufreq/policy4&#xff08;对应2个A76:CPU4-…

【Java从0到1学习】01 Java 概述

1. Java概述 Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称。由 James Gosling和同事们共同研发&#xff0c;并在 1995 年正式推出。 后来 Sun 公司被 Oracle &#xff08;甲骨文&#xff09;公司收购&#xff0c;Java…

微信小程序基于Promise封装发起网络请求

1.创建一个request.js // 相当于域名 const baseURL ***************; // 暴露一个request函数 export function request(parms) {// 路径拼接const url baseURL parms.url;// 请求体&#xff0c;默认为{}const data parms.data || {};// 请求方式&#xff0c;默认为GETco…

<Babel> 前端语言的巴别塔

Babel中文站点&#xff1a;https://www.babeljs.cn/ Babel外文站点&#xff1a;https://babeljs.io/ 什么是Babel Babel取自人类语言最早的传说&#xff0c;Tower of Babel。 上帝摧毁了巴别塔上说着共同语言的我们&#xff0c;又被Babel重新带了回来。 如果说巴别塔是人们对混…

windows命令行运行mysql

1.运行命令&#xff1a;mysql -u用户名 -p密码 2.创建数据库&#xff1a; create 数据库名称 &#xff1b; 3.use 数据库 4.show tables &#xff1b; 5.创建用户&#xff0c;分配权限 6.退出 ctrlc 7.切换用户

海外app在谷歌和苹果商店中该如何设置关键词

主导应用商店搜索结果的方法就是关键词的设置。我们需要寻找关键词&#xff0c;跟踪关键词排名并监控其应用的性能&#xff0c;这样就能大大的提高应用的可见度。 优先考虑可推动Android或ios应用自然下载量的关键词&#xff0c;使用搜索量指标了解某个关键词在应用商店中的搜…

数字孪生很火嘛?是做什么的?

数字孪生是一种新兴的技术概念&#xff0c;将现实世界与数字世界紧密结合&#xff0c;通过数字化的方式模拟、仿真和预测真实世界的物理实体、过程和系统。它是物理实体与其数字化的虚拟模型之间的一种互联关系&#xff0c;旨在实现对现实世界的全面感知和实时监测。 数字孪生的…

骑行健身,生活和工作压力的避风港

在忙碌的现代生活中&#xff0c;每个人都面临着种种生活压力和工作压力。而自行车运动&#xff0c;正是一种理想的压力释放方式。它不仅能帮助我们保持身体健康&#xff0c;更能丰富我们的生活&#xff0c;让我们在自然中寻找宁静。 首先&#xff0c;骑自行车是一种全身性的运动…

BigTable:一个针对结构化数据的分布式存储系统----论文摘要

目录 摘要 1. 介绍 2. 数据模型 行 列族 时间戳 3. API 4. 所需构件 5. 实现 5.1 Tablet的位置 5.2 Tablet分配 5.3 Tablet服务 5.4 压实&#xff08;Compactions&#xff09; 6. 优化 本地化分组 压缩(compression) 通过缓存提高读操作的性能 Bloom过滤器 C…

2023年7月16日,HashMap

HashMap HashMap存储的是一组无序的键值对。存储时是根据键的哈希码来计算存储的位置&#xff0c;因为对象的哈希码是不确定的&#xff0c;因此HashMap存储的元素是无序的。 Map用于保存具有映射关系的数据&#xff0c;Map里保存着两组数据&#xff1a;key和value&#xff0c;它…

Leetcode-每日一题【2487.从链表中移除节点】

题目 给你一个链表的头节点 head 。 对于列表中的每个节点 node &#xff0c;如果其右侧存在一个具有 严格更大 值的节点&#xff0c;则移除 node 。 返回修改后链表的头节点 head 。 示例 1&#xff1a; 输入&#xff1a;head [5,2,13,3,8]输出&#xff1a;[13,8]解释&…

拒绝被其他域名恶意解析到你的服务器上

拒绝被其他域名恶意解析到你的服务器上 备案问题恶意解析解决方案后记 备案问题 新的一周开始了&#xff0c;又是一个摸鱼的好时候。。。。结果&#xff0c;刚刚坐到工位上&#xff0c;机房客服发来了一个 excel &#xff0c;说。。。你的备案信息没完善。。。 啥&#xff1f…

C语言进阶之程序环境和预处理

程序环境和预处理 1. 程序的翻译环境和执行环境2. 详解编译链接2.1 翻译环境2.2 编译的几个阶段2.3 运行环境 3. 预处理详解3.1 预定义符号3.2 #define3.2.1 #define 定义标识符3.2.2 #define 定义宏3.2.3 #define 替换规则3.2.4 #和##3.2.5 带副作用的宏参数3.2.6 宏和函数对比…

快速搭建接口自动化测试框架

1 接口测试 接口测试是对系统或组件之间的接口进行测试&#xff0c;主要是校验数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及相互逻辑依赖关系。 接口自动化相对于UI自动化来说&#xff0c;属于更底层的测试&#xff0c;这样带来的好处就是测试收益更大&#xff…

Python基础教程:数据结构

Python是一门广泛使用的编程语言&#xff0c;它的丰富的数据结构使得编写程序变得异常简单和方便。在本篇教程中&#xff0c;我将详细介绍Python中的四种主要数据结构&#xff1a;列表、元组、字典、集合。 1.列表&#xff08;List&#xff09; 列表是Python中最常用的数据结…