From a9e02686a409c8cc798f0da4b123396ffb64ece3 Mon Sep 17 00:00:00 2001 From: "DESKTOP-JVKHS7I\\Adam" Date: Fri, 15 Feb 2019 19:14:49 +0530 Subject: [PATCH] abstracted control and log, removed bool from show method, windows scale bug fixed --- controls.py | 63 ++++++++++++++++++++++++++++++++ fishy.py | 103 ++++++++++++++++++++++++++++++++++++++++++++++------ init.py | 93 +++++++++++++++++++++++++++++++---------------- log.py | 37 +++++++++++++++++++ window.py | 41 +++++++++++++++++---- 5 files changed, 286 insertions(+), 51 deletions(-) create mode 100644 controls.py create mode 100644 log.py diff --git a/controls.py b/controls.py new file mode 100644 index 0000000..5ed168d --- /dev/null +++ b/controls.py @@ -0,0 +1,63 @@ +from init import * + + +class Control: + +#todo change to 0 + current = 1 if arguments["--debug"] else 0 + + class Keywords(Enum): + SwitchMode = "switch mode" + StartPause = "start/pause" + Debug = "debug" + Stop = "stop" + ConfigPixLoc = "config pixel location" + ClearPrintOnce = "clear print once" + + controls = [ + { + "name": "SYSTEM", + "controls": [ + [Keywords.SwitchMode, Key.f8], + [Keywords.StartPause, Key.f9], + [Keywords.ConfigPixLoc, Key.f10], + [Keywords.Stop, Key.f11] + ] + }, + { + "name": "DEBUG", + "controls": [ + [Keywords.SwitchMode, Key.f8], + [Keywords.ClearPrintOnce, Key.f9], + [Keywords.Debug, Key.f10], + ] + } + ] + + + @staticmethod + def getControlHelp(): + s = "\n\nCurrent Mode: " + Control.get()["name"]+"\n" + for c in Control.controls[Control.current]["controls"]: + s += c[0].value + ": " + c[1].name + "\n" + + return s + + @staticmethod + def get(): + return Control.controls[Control.current] + + @staticmethod + def find(key): + for c in Control.get()["controls"]: + if key == c[1]: + return c + + return None + + @staticmethod + def nextState(): + Control.current += 1 + + if Control.current >= len(Control.controls): + Control.current = 0 diff --git a/fishy.py b/fishy.py index 90854f4..21d8260 100644 --- a/fishy.py +++ b/fishy.py @@ -8,22 +8,74 @@ def on_release(key): :return: void """ - if G.controls["pause"][0] == key: + c = Control.find(key) + if c is None: + return + + if c[0] == Control.Keywords.StartPause: G.pause = not G.pause if G.pause: print("PAUSED") else: print("STARTED") - elif G.controls["debug"][0] == key: + elif c[0] == Control.Keywords.Debug: G.debug = not G.debug - elif G.controls["stop"][0] == key: + elif c[0] == Control.Keywords.Stop: G.stop = True - elif G.controls["configPL"][0] == key: + elif c[0] == Control.Keywords.ConfigPixLoc: G.configPL = not G.configPL + elif c[0] == Control.Keywords.SwitchMode: + Control.nextState() + Log.ctrl() + + elif c[0] == Control.Keywords.ClearPrintOnce: + Log.clearPrintIds() + + +def ipDebug(img): + # Setup SimpleBlobDetector parameters. + hsvImg = cv2.cvtColor(img, cv2.COLOR_RGB2HSV) + lower = (99, 254, 100) + upper = (100, 255, 101) + mask = cv2.inRange(hsvImg, lower, upper) + #mask = cv2.bitwise_not(mask) + + # Setup SimpleBlobDetector parameters. + params = cv2.SimpleBlobDetector_Params() + + # Change thresholds + params.minThreshold = 10 + params.maxThreshold = 255 + + params.filterByColor = True + params.blobColor = 255 + + params.filterByCircularity = False + params.filterByConvexity = False + params.filterByInertia = False + + params.filterByArea = True + params.minArea = 10.0 + + detector = cv2.SimpleBlobDetector_create(params) + + # Detect blobs. + keypoints = detector.detect(mask) + + # Draw detected blobs as red circles. + # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob + draw_keypoints(img, keypoints) + + return img + + +def hsv2rgb(img): + return cv2.cvtColor(img, cv2.COLOR_HSV2RGB) + def startFishing(): """ @@ -45,30 +97,57 @@ def startFishing(): FishingMode("look", 2, LookEvent()) FishingMode("idle", 3, IdleEvent(use_net)) - fishPixWindow = Window("fishPixWindow", PixelLoc.val, 1, cv2.COLOR_BGR2HSV) - fishPixDebugWindow = Window("fishPixDebugWindow", PixelLoc.val, 200, cv2.COLOR_BGR2RGB) + fishPixWindow = Window("fishPixWindow", PixelLoc.val, cv2.COLOR_BGR2HSV) + + try: + hwnd = win32gui.FindWindow(None, "Elder Scrolls Online") + + rect = win32gui.GetWindowRect(hwnd) + clientRect = win32gui.GetClientRect(hwnd) + windowOffset = math.floor(((rect[2] - rect[0]) - clientRect[2]) / 2) + titleOffset = ((rect[3] - rect[1]) - clientRect[3]) - windowOffset + windowLoc = (rect[0] + windowOffset, rect[1] + titleOffset, rect[2] - windowOffset, rect[3] - windowOffset) + + gameScreen = Window("game", windowLoc, cv2.COLOR_BGR2RGB) + + except pywintypes.error: + print("Game window not found") + return Log.ctrl() # todo + time.time() + with Listener(on_release=on_release): while not G.stop: - time.sleep(sleepFor) + current_time = time.time() + Window.Loop() Log.Loop() + PixelLoc.Loop() pixelVal = (PixelLoc.val[0], PixelLoc.val[1], PixelLoc.val[0] + 1, PixelLoc.val[1] + 1) fishPixWindow.crop = pixelVal - fishPixDebugWindow.crop = pixelVal hueValue = fishPixWindow.getCapture()[0][0][0] FishingMode.Loop(hueValue, G.pause) - fishPixDebugWindow.show(G.debug or G.configPL) - if G.debug or G.configPL: - Log.ou(str(FishingMode.CurrentMode.label) + ":" + str(hueValue)) - PixelLoc.Loop() + + if G.configPL: + fishPixWindow.show(resize=200, func=hsv2rgb) + Log.ou(str(FishingMode.CurrentMode.label) + ":" + str(fishPixWindow.getCapture()[0][0])) + + if G.debug: + rect = win32gui.GetWindowRect(hwnd) + gameScreen.crop = (rect[0] + windowOffset, rect[1] + titleOffset, rect[2] - windowOffset, + rect[3] - windowOffset) + gameScreen.show(func=ipDebug) Log.LoopEnd() Window.LoopEnd() + frameTime = time.time() - current_time + if frameTime < sleepFor: + time.sleep(sleepFor - frameTime) + if __name__ == "__main__": startFishing() diff --git a/init.py b/init.py index 480e3ce..78cac6f 100644 --- a/init.py +++ b/init.py @@ -3,14 +3,15 @@ Usage: fishy.py -h | --help fishy.py -v | --version - fishy.py [--ip=] [--hook-threshold=] [--check-frequency=] [--no-resize] + fishy.py [--debug] [--ip=] [--hook-threshold=] [--check-frequency=] [--no-resize] Options: -h, --help Show this screen. -v, --version Show version. --ip= Local Ip Address of the android phone. - --hook-threshold= Threshold amount for classifier after which label changes [default: 3]. - --check-frequency= Sleep after loop in s [default: 10]. + --hook-threshold= Threshold amount for classifier after which label changes [default: 1]. + --check-frequency= Sleep after loop in s [default: 1]. + --debug Start program in debug controls. """ VERSION = "0.1.0" @@ -37,51 +38,81 @@ try: from win32api import GetSystemMetrics import pickle import win32gui + import pywintypes from abc import ABC, abstractmethod + from enum import Enum + import sys + import numpy as np + import math except Exception: raise +''' +import stack + +fishy +pixel_loc +fishing_event +fishing_mode +window +log +controls +init +''' + class G: fishCaught = 0 stickInitTime = 0 - controls = {"stop": [Key.f11, "f11"], "debug": [Key.f10, "f10"], "pause": [Key.f9, "f9"], - "configPL": [Key.f8, "f8"]} stop = False pause = True debug = False configPL = False -class Log: - ouUsed = False - prevOuUsed = False - ctrl_help = G.controls["configPL"][1] + ": config pixel value\n" + G.controls["pause"][1] + ": start or pause\n" + \ - G.controls["debug"][1] + ": start debug\n" + G.controls["stop"][1] + ": quit\n" - - @staticmethod - def Loop(): - Log.ouUsed = False - - @staticmethod - def LoopEnd(): - if Log.prevOuUsed and not Log.ouUsed: - print(Log.ctrl_help) - Log.prevOuUsed = Log.ouUsed - - @staticmethod - def ctrl(): - print(Log.ctrl_help) - - @staticmethod - def ou(s): - Log.ouUsed = True - print(s) - - def round_float(v, ndigits=2, rt_str=False): d = Decimal(v) v_str = ("{0:.%sf}" % ndigits).format(round(d, ndigits)) if rt_str: return v_str return Decimal(v_str) + + +def draw_keypoints(vis, keypoints, color=(0, 0, 255)): + for kp in keypoints: + x, y = kp.pt + cv2.circle(vis, (int(x), int(y)), 5, color, -1) + + +def image_resize(image, width=None, height=None, inter = cv2.INTER_AREA): + # initialize the dimensions of the image to be resized and + # grab the image size + dim = None + (h, w) = image.shape[:2] + + # if both the width and height are None, then return the + # original image + if width is None and height is None: + return image + + # check to see if the width is None + if width is None: + # calculate the ratio of the height and construct the + # dimensions + r = height / float(h) + dim = (int(w * r), height) + + # otherwise, the height is None + else: + # calculate the ratio of the width and construct the + # dimensions + r = width / float(w) + dim = (width, int(h * r)) + + # resize the image + resized = cv2.resize(image, dim, interpolation = inter) + + # return the resized image + return resized + +# np.set_printoptions(threshold=sys.maxsize) \ No newline at end of file diff --git a/log.py b/log.py new file mode 100644 index 0000000..6c27493 --- /dev/null +++ b/log.py @@ -0,0 +1,37 @@ +from controls import * + + +class Log: + ouUsed = False + prevOuUsed = False + printIds = [] + + @staticmethod + def Loop(): + Log.ouUsed = False + + @staticmethod + def LoopEnd(): + if Log.prevOuUsed and not Log.ouUsed: + Log.ctrl() + Log.prevOuUsed = Log.ouUsed + + @staticmethod + def ctrl(): + print(Control.getControlHelp()) + + @staticmethod + def ou(s): + Log.ouUsed = True + print(s) + + @staticmethod + def po(id, s): + if id not in Log.printIds: + print(s) + Log.printIds.append(id) + + @staticmethod + def clearPrintIds(): + Log.printIds = [] + diff --git a/window.py b/window.py index 02e3c64..f419437 100644 --- a/window.py +++ b/window.py @@ -1,32 +1,57 @@ -from init import * +from log import * class Window: Screen = None + wins = [] - def __init__(self, name, crop, scale, color): + def __init__(self, name, crop, color, scale=None): self.color = color self.crop = crop self.scale = scale self.name = name + self.showed = False + Window.wins.append(self) + @staticmethod def Loop(): bbox = (0, 0, GetSystemMetrics(0), GetSystemMetrics(1)) Window.Screen = np.array(ImageGrab.grab(bbox=bbox)) + @staticmethod def LoopEnd(): cv2.waitKey(25) - def show(self, show): - if show: - cv2.imshow(self.name, self.getCapture()) - else: - cv2.destroyWindow(self.name) + for w in Window.wins: + + if not w.showed: + cv2.destroyWindow(w.name) + + w.showed = False def getCapture(self): temp_img = cv2.cvtColor(Window.Screen, self.color) temp_img = temp_img[self.crop[1]:self.crop[3], self.crop[0]:self.crop[2]] - temp_img = imutils.resize(temp_img, width=self.scale) + + if self.scale is not None: + temp_img = cv2.resize(temp_img, (self.scale[0], self.scale[1]), interpolation=cv2.INTER_AREA) + return temp_img + + def processedImage(self, func=None): + if func is None: + return self.getCapture() + else: + return func(self.getCapture()) + + def show(self, resize=None, func=None): + img = self.processedImage(func) + + if resize is not None: + img = imutils.resize(img, width=resize) + + cv2.imshow(self.name, img) + + self.showed = True