Ejemplo n.º 1
0
    def start_macro(self):
        if not self.macro_process:
            self.toggle_macro_process()
        keymap = self.get_keymap()
        if not keymap:
            showerror(APP_TITLE, "키설정을 읽어오지 못했습니다. 키를 다시 설정해주세요.")
        else:
            if not self.platform_file_dir.get():
                showwarning(APP_TITLE, "지형 파일을 선택해 주세요.")
            else:
                if not MapleScreenCapturer().ms_get_screen_hwnd():
                    showwarning(APP_TITLE, "메이플 창을 찾지 못했습니다. 메이플을 실행해 주세요")
                else:

                    cap = MapleScreenCapturer()
                    hwnd = cap.ms_get_screen_hwnd()
                    rect = cap.ms_get_screen_rect(hwnd)
                    self.log("MS hwnd", hwnd)
                    self.log("MS rect", rect)
                    self.log("Out Queue put:", self.platform_file_dir.get())
                    if rect[0] < 0 or rect[1] < 0:
                        showwarning(
                            APP_TITLE,
                            "메이플 창 위치를 가져오는데 실패했습니다.\n메이플 촹의 좌측 상단 코너가 화면 내에 있도록 메이플 창을 움직여주세요."
                        )

                    else:
                        cap.capture()
                        self.macro_process_out_queue.put(
                            ("start", keymap, self.platform_file_dir.get()))
                        self.macro_start_button.configure(state=DISABLED)
                        self.macro_end_button.configure(state=NORMAL)
                        self.platform_file_button.configure(state=DISABLED)
Ejemplo n.º 2
0
    def start_macro(self):
        # print(MapleScreenCapturer().ms_get_screen_hwnd())
        if not self.macro_process:
            self.toggle_macro_process()
        keymap = self.get_keymap()
        if not keymap:
            showerror(APP_TITLE,
                      "Failed to read key settings. Please reset the key.")
        else:
            if not self.platform_file_dir.get():
                showwarning(APP_TITLE, "Please select a terrain file.")
            else:
                if not MapleScreenCapturer().ms_get_screen_hwnd():
                    showwarning(
                        APP_TITLE,
                        "Maple window was not found. Please run Maple")
                else:

                    cap = MapleScreenCapturer()
                    hwnd = cap.ms_get_screen_hwnd()
                    rect = cap.ms_get_screen_rect(hwnd)
                    self.log("MS hwnd", hwnd)
                    self.log("MS rect", rect)
                    self.log("Out Queue put:", self.platform_file_dir.get())
                    if rect[0] < 0 or rect[1] < 0:
                        showwarning(
                            APP_TITLE,
                            "Failed to get the location of the Maple window.\nMove the Maple window so that the top left corner of the Maple is within the screen.."
                        )

                    else:
                        cap.capture()
                        self.macro_process_out_queue.put(
                            ("start", keymap, self.platform_file_dir.get()))
                        self.macro_start_button.configure(state=DISABLED)
                        self.macro_end_button.configure(state=NORMAL)
                        self.platform_file_button.configure(state=DISABLED)
Ejemplo n.º 3
0
import cv2, time, imutils, math, glob, random
import numpy as np
cap = MapleScreenCapturer()
from win32gui import SetForegroundWindow

x, y, w, h = 450, 180, 500, 130
ds = None
while True:
    img = cap.capture(rect=[0, 0, 1600, 900], set_focus=False)
    img_arr = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
    final_img = imutils.resize(img_arr, width=200)
    cv2.imshow("s to save image", final_img)
    inp = cv2.waitKey(1)

    if inp == ord("s"):
        SetForegroundWindow(cap.ms_get_screen_hwnd())
        time.sleep(0.3)
        ds = cap.capture(set_focus=False)
        ds = cv2.cvtColor(np.array(ds), cv2.COLOR_RGB2BGR)
        ds = ds[y:y + h, x:x + w]
        print("saved")

    elif inp == ord("q"):
        cv2.destroyAllWindows()
        break
    elif inp == ord("r"):
        imgpath = "C:\\Users\\tttll\\PycharmProjects\\MacroSTory\\rune_trainer\\images\\screenshots\\finished\\*.png"
        dt = random.choice(glob.glob(imgpath))

        ds = cv2.imread(dt)
        print("read data")
Ejemplo n.º 4
0
class RuneDetector:
    def __init__(self,
                 model_path,
                 labels=None,
                 screen_capturer=None,
                 key_mgr=None):
        """
        Run just Once to initialize
        :param model_path: Path to trained keras model
        :param labels: dictionary with class names as keys, integer as values
        example: {'down': 0, 'left': 1, 'right': 2, 'up': 3}
        """
        self.logger = logging.getLogger("RuneDetector")
        self.logger.setLevel(logging.DEBUG)

        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s')

        fh = logging.FileHandler("logging.log")
        fh.setLevel(logging.DEBUG)
        fh.setFormatter(formatter)
        self.logger.addHandler(fh)
        self.labels = labels if labels else {
            'down': 0,
            'left': 1,
            'right': 2,
            'up': 3
        }
        self.model_path = model_path
        with device("/cpu:0"):  # Use cpu for evaluation
            model = load_model(self.model_path)
            #model.compile(optimizer="adam", loss='categorical_crossentropy', metrics=['accuracy'])
            model.load_weights(self.model_path)

        self.model = model

        self.rune_roi_1366 = [450, 180, 500, 130]  # x, y, w, h
        self.rune_roi_1024 = [295, 180, 500, 133]
        self.rune_roi_800 = [170, 200, 440, 135]
        self.rune_roi = self.rune_roi_800  # set as default rune roi
        self.screen_processor = MapleScreenCapturer(
        ) if not screen_capturer else screen_capturer
        self.key_mgr = KeyboardInputManager() if not key_mgr else key_mgr

    def capture_roi(self):
        screen_rect = self.screen_processor.ms_get_screen_rect(
            self.screen_processor.ms_get_screen_hwnd())
        screen_width = screen_rect[2] - screen_rect[0]

        if screen_width > 1300:
            self.rune_roi = self.rune_roi_1366
        elif screen_width > 1000:
            self.rune_roi = self.rune_roi_1024
        elif screen_width > 800:
            self.rune_roi = self.rune_roi_800

        captured_image = self.screen_processor.capture(set_focus=False,
                                                       rect=screen_rect)

        captured_roi = cv2.cvtColor(np.array(captured_image),
                                    cv2.COLOR_RGB2BGR)

        captured_roi = captured_roi[self.rune_roi[1]:self.rune_roi[1] +
                                    self.rune_roi[3],
                                    self.rune_roi[0]:self.rune_roi[0] +
                                    self.rune_roi[2]]

        return captured_roi

    def preprocess(self, img):
        """
        finds and returns sorted list of 60 by 60 grayscale images of circles, centered
        :param img: BGR image of roi containing circle
        :return: list of grayscale images each containing a circle
        """
        hsv_img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
        hsv_img[:, :, 1] = 255
        hsv_img[:, :, 2] = 255
        bgr_img = cv2.cvtColor(hsv_img, cv2.COLOR_HSV2BGR)
        gray_img = cv2.cvtColor(bgr_img, cv2.COLOR_BGR2GRAY)

        circles = cv2.HoughCircles(gray_img,
                                   cv2.HOUGH_GRADIENT,
                                   1,
                                   gray_img.shape[0] / 8,
                                   param1=100,
                                   param2=30,
                                   minRadius=18,
                                   maxRadius=30)
        temp_list = []
        img_index = 1
        if circles is not None:
            circles = np.round(circles[0, :]).astype("int")
            for (x, y, r) in circles:

                cropped = gray_img[max(0, int(y - 60 / 2)):int(y + 60 / 2),
                                   max(0, int(x - 60 / 2)):int(x +
                                                               60 / 2)].astype(
                                                                   np.float32)

                temp_list.append((cropped, (x, y)))
                img_index += 1

        temp_list = sorted(temp_list, key=lambda x: x[1][0])
        return_list = []
        for image in temp_list:
            return_list.append(image[0])

        return return_list

    def images2tensor(self, img_list):
        """
        Creates a tf compliant tensor by stacking images in img_list
        :param img_list:
        :return: np.array of shape [1, 60, 60, 1]
        """
        return np.vstack([np.reshape(x, [1, 60, 60, 1]) for x in img_list])

    def classify(self, tensor, batch_size=4):
        """
        Runs tensor through model and returns list of direction in string.
        :param tensor: input tensor
        :param batch_size: batch size
        :return: size of strings "up", "down", "left", "right"
        """
        return_list = []
        result = self.model.predict(tensor, batch_size=batch_size)
        for res in result:
            final_class = np.argmax(res, axis=-1)
            for key, val in self.labels.items():
                if final_class == val:
                    return_list.append(key)

        return return_list

    def solve_auto(self):
        """
        Solves rune if present and sends key presses.
        :return: -1 if rune not detected, result of classify() if successful
        """
        img = self.capture_roi()
        processed_imgs = self.preprocess(img)
        if len(processed_imgs) != 4:
            self.logger.debug("Failed to extract 4 ROI from processed image")
            return -1
        #cv2.imwrite("roi.png", img)
        tensor = self.images2tensor(processed_imgs)
        result = self.classify(tensor)
        if GetKeyState(VK_NUMLOCK):
            self.key_mgr.single_press(DIK_NUMLOCK)
            time.sleep(0.2)
        self.logger.debug("Solved rune with solution %s" % (str(result)))
        for inp in result:
            if inp == "up":
                self.key_mgr.single_press(DIK_UP)
            elif inp == "down":
                self.key_mgr.single_press(DIK_DOWN)
            elif inp == "left":
                self.key_mgr.single_press(DIK_LEFT)
            elif inp == "right":
                self.key_mgr.single_press(DIK_RIGHT)
            time.sleep(0.1)
        return len(processed_imgs)

    def press_space(self):
        self.key_mgr.single_press(DIK_SPACE)

    def solve(self):
        """
        Solves rune if present and just returns solution.
        :return: -1 if rune not detected, result of classify() if successful
        """
        img = self.capture_roi()
        processed_imgs = self.preprocess(img)
        if len(processed_imgs) != 4:
            return -1
        tensor = self.images2tensor(processed_imgs)
        result = self.classify(tensor)

        return result
Ejemplo n.º 5
0
class PlatformDataCaptureWindow(tk.Toplevel):
    def __init__(self):
        tk.Toplevel.__init__(self)
        self.wm_minsize(100, 30)
        self.resizable(0, 0)
        self.focus_get()
        self.grab_set()
        self.title("지형파일 생성기")

        self.last_coord_x = None
        self.last_coord_y = None
        self.current_coords = []

        self.screen_capturer = MapleScreenCapturer()
        if not self.screen_capturer.ms_get_screen_hwnd():
            showerror("지형파일 생성기", "메이플 창을 찾지 못했습니다. 메이플을 실행해 주세요.")
            self.destroy()
        else:
            self.image_processor = StaticImageProcessor(self.screen_capturer)
            self.terrain_analyzer = PathAnalyzer()
            self.image_label = tk.Label(self)
            self.image_label.pack(expand=YES, fill=BOTH)

            self.master_tool_frame = tk.Frame(self,
                                              borderwidth=2,
                                              relief=GROOVE)
            self.master_tool_frame.pack(expand=YES, fill=BOTH)

            self.tool_frame_1 = tk.Frame(self.master_tool_frame)
            self.tool_frame_1.pack(fill=X)
            tk.Button(self.tool_frame_1,
                      text="창 및 미니맵 다시 찾기",
                      command=self.find_minimap_coords).pack(side=LEFT)
            self.coord_label = tk.Label(self.tool_frame_1, text="x,y")
            self.coord_label.pack(side=RIGHT, fill=Y, expand=YES)

            self.tool_frame_2 = tk.Frame(self.master_tool_frame)
            self.tool_frame_2.pack(fill=X)
            self.start_platform_record_button = tk.Button(
                self.tool_frame_2,
                text="스폰지형 기록시작",
                command=self.start_record_platform)
            self.start_platform_record_button.pack(side=LEFT,
                                                   expand=YES,
                                                   fill=X)
            self.stop_platform_record_button = tk.Button(
                self.tool_frame_2,
                text="스폰지형 기록중지",
                command=self.stop_record_platform,
                state=DISABLED)
            self.stop_platform_record_button.pack(side=RIGHT,
                                                  expand=YES,
                                                  fill=X)

            self.tool_frame_3 = tk.Frame(self.master_tool_frame)
            self.tool_frame_3.pack(fill=X)
            self.start_oneway_record_button = tk.Button(
                self.tool_frame_3,
                text="비스폰지형 기록시작",
                command=self.start_record_oneway)
            self.start_oneway_record_button.pack(side=LEFT, expand=YES, fill=X)
            self.stop_oneway_record_button = tk.Button(
                self.tool_frame_3,
                text="비스폰지형 기록중지",
                command=self.stop_record_oneway,
                state=DISABLED)
            self.stop_oneway_record_button.pack(side=RIGHT, expand=YES, fill=X)

            self.tool_frame_4 = tk.Frame(self.master_tool_frame)
            self.tool_frame_4.pack(fill=X, side=BOTTOM)
            tk.Button(self.tool_frame_4,
                      text="초기화",
                      command=self.on_reset_platforms).pack(side=LEFT,
                                                            expand=YES,
                                                            fill=X)
            tk.Button(self.tool_frame_4, text="저장하기",
                      command=self.on_save).pack(side=RIGHT,
                                                 expand=YES,
                                                 fill=X)

            self.platform_listbox = tk.Listbox(self, selectmode=MULTIPLE)
            self.platform_listbox.pack(expand=YES, fill=BOTH)
            self.platform_listbox_platform_index = {}
            self.platform_listbox_oneway_index = {}
            self.platform_listbox.bind("<Button-3>",
                                       self.on_platform_list_rclick)

            self.platform_listbox_menu = tk.Menu(self, tearoff=0)
            self.platform_listbox_menu.add_command(
                label="선택된 항목 삭제", command=self.on_listbox_delete)

            self.image_processor.update_image(set_focus=False)
            self.minimap_rect = self.image_processor.get_minimap_rect()
            if not self.minimap_rect:
                self.image_label.configure(text="미니맵 찾을수 없음", fg="red")

            self.stopEvent = threading.Event()
            self.thread = threading.Thread(target=self.update_image, args=())
            self.thread.start()

            self.record_mode = 0  # 0 if not recording, 1 if normal platform, 2 if oneway

            self.protocol("WM_DELETE_WINDOW", self.onClose)

    def onClose(self):
        self.stopEvent.set()
        self.after(200, self.destroy)

    def on_platform_list_rclick(self, event):
        try:
            self.platform_listbox_menu.tk_popup(event.x_root, event.y_root, 0)
        finally:
            self.platform_listbox_menu.grab_release()

    def on_listbox_delete(self):
        selected = self.platform_listbox.curselection()
        if not selected:
            showwarning("지형파일 생성기", "한개 이상의 항목을 선택해 주세요")
        else:
            if askyesno("지형파일 생성기", "정말 %d개의 항목을 지우겠습니까?" % (len(selected))):
                if self.record_mode != 0:
                    showwarning("지형파일 생성기", "진행중인 기록 먼저 종료해주세요")
                else:
                    for idx in selected:
                        for key, hash in self.platform_listbox_platform_index.items(
                        ):
                            if idx == key:
                                del self.terrain_analyzer.platforms[hash]

                        for key, hash in self.platform_listbox_oneway_index.items(
                        ):
                            if idx == key:
                                del self.terrain_analyzer.oneway_platforms[
                                    hash]
                    self.update_listbox()

    def update_listbox(self):
        self.platform_listbox_platform_index = {}
        self.platform_listbox_oneway_index = {}
        self.platform_listbox.delete(0, END)
        cindex = 0
        for key, platform in self.terrain_analyzer.platforms.items():
            self.platform_listbox.insert(
                END,
                "(%d,%d), (%d,%d) 스폰지형" % (platform.start_x, platform.start_y,
                                           platform.end_x, platform.end_y))
            self.platform_listbox_platform_index[cindex] = key
            self.platform_listbox.itemconfigure(cindex, fg="green")
            cindex += 1

        for key, platform in self.terrain_analyzer.oneway_platforms.items():
            self.platform_listbox.insert(
                END,
                "(%d,%d), (%d,%d) 비스폰지형" % (platform.start_x, platform.start_y,
                                            platform.end_x, platform.end_y))
            self.platform_listbox_oneway_index[cindex] = key
            self.platform_listbox.itemconfigure(cindex, fg="red")
            cindex += 1

    def start_record_platform(self):
        self.record_mode = 1
        self.start_platform_record_button.configure(state=DISABLED)
        self.stop_platform_record_button.configure(state=NORMAL)
        self.start_oneway_record_button.configure(state=DISABLED)
        self.stop_oneway_record_button.configure(state=DISABLED)

    def stop_record_platform(self):
        self.record_mode = 0
        self.start_platform_record_button.configure(state=NORMAL)
        self.stop_platform_record_button.configure(state=DISABLED)
        self.start_oneway_record_button.configure(state=NORMAL)
        self.stop_oneway_record_button.configure(state=DISABLED)
        self.coord_label.configure(fg="black")
        self.terrain_analyzer.flush_input_coords_to_platform(
            coord_list=self.current_coords)
        self.current_coords = []
        self.update_listbox()

    def start_record_oneway(self):
        self.record_mode = 2
        self.start_platform_record_button.configure(state=DISABLED)
        self.stop_platform_record_button.configure(state=DISABLED)
        self.start_oneway_record_button.configure(state=DISABLED)
        self.stop_oneway_record_button.configure(state=NORMAL)

    def stop_record_oneway(self):
        self.record_mode = 0
        self.start_platform_record_button.configure(state=NORMAL)
        self.stop_platform_record_button.configure(state=DISABLED)
        self.start_oneway_record_button.configure(state=NORMAL)
        self.stop_oneway_record_button.configure(state=DISABLED)
        self.coord_label.configure(fg="black")
        self.terrain_analyzer.flush_input_coords_to_oneway(
            coord_list=self.current_coords)
        self.current_coords = []
        self.update_listbox()

    def on_save(self):
        save_dir = asksaveasfilename(initialdir=os.getcwd(),
                                     title="저장경로 설정",
                                     filetypes=(("지형 파일(*.platform)",
                                                 "*.platform"), ))
        if save_dir:
            if ".platform" not in save_dir:
                save_dir += ".platform"
            self.terrain_analyzer.save(save_dir, self.minimap_rect)
            showinfo("지형파일 생성기", "파일경로 {0}\n 저장되었습니다.".format(save_dir))
            self.onClose()

    def on_reset_platforms(self):
        if askyesno("지형파일 생성기", "정말 모든 지형들을 삭제할까요?"):
            self.record_mode = 0
            self.coord_label.configure(fg="black")
            self.terrain_analyzer.reset()
            self.start_platform_record_button.configure(state=NORMAL)
            self.stop_platform_record_button.configure(state=DISABLED)
            self.start_oneway_record_button.configure(state=NORMAL)
            self.stop_oneway_record_button.configure(state=DISABLED)
            self.update_listbox()

    def find_minimap_coords(self):
        self.image_processor.update_image(set_focus=False, update_rect=True)
        self.minimap_rect = self.image_processor.get_minimap_rect()

    def update_image(self):
        while not self.stopEvent.is_set():
            self.image_processor.update_image(set_focus=False)
            if not self.minimap_rect:
                self.image_label.configure(text="미니맵 찾을수 없음", fg="red")
                self.find_minimap_coords()
                continue

            playerpos = self.image_processor.find_player_minimap_marker(
                self.minimap_rect)

            if not playerpos:
                self.image_label.configure(text="플레이어 위치 찾을수 없음", fg="red")
                self.find_minimap_coords()
                continue

            self.last_coord_x, self.last_coord_y = playerpos
            if self.record_mode == 1 or self.record_mode == 2:
                if (self.last_coord_x,
                        self.last_coord_y) not in self.current_coords:
                    self.current_coords.append(
                        (self.last_coord_x, self.last_coord_y))
                if self.record_mode == 1:
                    self.coord_label.configure(fg="green")
                elif self.record_mode == 2:
                    self.coord_label.configure(fg="red")

            self.coord_label.configure(text="%d,%d" %
                                       (playerpos[0], playerpos[1]))
            if self.minimap_rect == 0:
                continue

            cropped_img = cv2.cvtColor(
                self.image_processor.bgr_img[
                    self.minimap_rect[1]:self.minimap_rect[1] +
                    self.minimap_rect[3],
                    self.minimap_rect[0]:self.minimap_rect[0] +
                    self.minimap_rect[2]], cv2.COLOR_BGR2RGB)
            if self.record_mode:
                cv2.line(cropped_img, (playerpos[0], 0),
                         (playerpos[0], cropped_img.shape[0]), (0, 0, 255), 1)
                cv2.line(cropped_img, (0, playerpos[1]),
                         (cropped_img.shape[1], playerpos[1]), (0, 0, 255), 1)
            else:
                cv2.line(cropped_img, (playerpos[0], 0),
                         (playerpos[0], cropped_img.shape[0]), (0, 0, 0), 1)
                cv2.line(cropped_img, (0, playerpos[1]),
                         (cropped_img.shape[1], playerpos[1]), (0, 0, 0), 1)

            selected = self.platform_listbox.curselection()
            if selected:
                for idx in selected:
                    for key, hash in self.platform_listbox_platform_index.items(
                    ):
                        if idx == key:
                            platform_obj = self.terrain_analyzer.platforms[
                                hash]
                            cv2.line(
                                cropped_img,
                                (platform_obj.start_x, platform_obj.start_y),
                                (platform_obj.end_x, platform_obj.end_y),
                                (0, 255, 0), 2)
                            break
                    for key, hash in self.platform_listbox_oneway_index.items(
                    ):
                        if idx == key:
                            platform_obj = self.terrain_analyzer.oneway_platforms[
                                hash]
                            cv2.line(
                                cropped_img,
                                (platform_obj.start_x, platform_obj.start_y),
                                (platform_obj.end_x, platform_obj.end_y),
                                (255, 0, 0), 2)
                            break
            else:
                for key, platform in self.terrain_analyzer.platforms.items():
                    cv2.line(cropped_img, (platform.start_x, platform.start_y),
                             (platform.end_x, platform.end_y), (0, 255, 0), 2)
                for key, platform in self.terrain_analyzer.oneway_platforms.items(
                ):
                    cv2.line(cropped_img, (platform.start_x, platform.start_y),
                             (platform.end_x, platform.end_y), (255, 0, 0), 2)

            img = Image.fromarray(cropped_img)
            img_tk = ImageTk.PhotoImage(image=img)
            self.image_label.image = img_tk
            self.image_label.configure(image=img_tk)

            self.update()