From 8ae46dd5a5e13c7e2b00b7434323490138c7cb53 Mon Sep 17 00:00:00 2001 From: Adam Saudagar Date: Sun, 21 Nov 2021 12:42:14 +0530 Subject: [PATCH] integrated fishyqr v0.0.2 for semi fisher - remove color detection code - move qr detection from full auto into common - refactor both engines to use qr from common --- .../qr_detection.py | 11 +++- fishy/engine/common/window_server.py | 8 ++- fishy/engine/fullautofisher/engine.py | 14 +--- fishy/engine/semifisher/engine.py | 37 +++++++---- fishy/engine/semifisher/fishing_event.py | 5 +- fishy/engine/semifisher/fishing_mode.py | 24 +------ fishy/engine/semifisher/pixel_loc.py | 66 ------------------- 7 files changed, 49 insertions(+), 116 deletions(-) rename fishy/engine/{fullautofisher => common}/qr_detection.py (81%) delete mode 100644 fishy/engine/semifisher/pixel_loc.py diff --git a/fishy/engine/fullautofisher/qr_detection.py b/fishy/engine/common/qr_detection.py similarity index 81% rename from fishy/engine/fullautofisher/qr_detection.py rename to fishy/engine/common/qr_detection.py index 2c969e4..4bd33e5 100644 --- a/fishy/engine/fullautofisher/qr_detection.py +++ b/fishy/engine/common/qr_detection.py @@ -9,6 +9,15 @@ from pyzbar.pyzbar import decode from fishy.helper.helper import get_documents +def image_pre_process(img): + scale_percent = 100 # percent of original size + width = int(img.shape[1] * scale_percent / 100) + height = int(img.shape[0] * scale_percent / 100) + dim = (width, height) + img = cv2.resize(img, dim, interpolation=cv2.INTER_AREA) + return img + + def get_qr_location(og_img): """ code from https://stackoverflow.com/a/45770227/4512396 @@ -40,7 +49,7 @@ def get_values_from_image(img): try: for qr in decode(img): vals = qr.data.decode('utf-8').split(",") - return float(vals[0]), float(vals[1]), float(vals[2]) + return tuple(float(v) for v in vals) logging.error("FishyQR not found") return None diff --git a/fishy/engine/common/window_server.py b/fishy/engine/common/window_server.py index bad4b83..ad433db 100644 --- a/fishy/engine/common/window_server.py +++ b/fishy/engine/common/window_server.py @@ -1,5 +1,6 @@ import logging import math +import traceback from enum import Enum from threading import Thread @@ -75,10 +76,15 @@ def loop_end(): cv2.waitKey(25) +# noinspection PyBroadException def run(): # todo use config while WindowServer.status == Status.RUNNING: - loop() + try: + loop() + except Exception: + traceback.print_exc() + WindowServer.status = Status.CRASHED loop_end() diff --git a/fishy/engine/fullautofisher/engine.py b/fishy/engine/fullautofisher/engine.py index 9a04e04..d929131 100644 --- a/fishy/engine/fullautofisher/engine.py +++ b/fishy/engine/fullautofisher/engine.py @@ -1,4 +1,3 @@ -import math import logging import math import time @@ -16,8 +15,8 @@ from fishy.engine.fullautofisher.mode.calibrator import Calibrator from fishy.engine.fullautofisher.mode.imode import FullAutoMode from fishy.engine.fullautofisher.mode.player import Player from fishy.engine.fullautofisher.mode.recorder import Recorder -from fishy.engine.fullautofisher.qr_detection import (get_qr_location, - get_values_from_image) +from fishy.engine.common.qr_detection import (get_qr_location, + get_values_from_image, image_pre_process) from fishy.engine.semifisher import fishing_event, fishing_mode from fishy.engine.semifisher.fishing_mode import FishingMode from fishy.helper import helper, hotkey @@ -29,15 +28,6 @@ mse = mouse.Controller() kb = keyboard.Controller() -def image_pre_process(img): - scale_percent = 100 # percent of original size - width = int(img.shape[1] * scale_percent / 100) - height = int(img.shape[0] * scale_percent / 100) - dim = (width, height) - img = cv2.resize(img, dim, interpolation=cv2.INTER_AREA) - return img - - class FullAuto(IEngine): rotate_by = 30 diff --git a/fishy/engine/semifisher/engine.py b/fishy/engine/semifisher/engine.py index e42e9a0..4e0be35 100644 --- a/fishy/engine/semifisher/engine.py +++ b/fishy/engine/semifisher/engine.py @@ -4,14 +4,16 @@ import typing from threading import Thread from typing import Callable, Optional +import cv2 + +from fishy.helper.helper import log_raise from playsound import playsound from fishy.engine.common.IEngine import IEngine +from fishy.engine.common.qr_detection import get_qr_location, get_values_from_image, image_pre_process from fishy.engine.common.window import WindowClient from fishy.engine.semifisher import fishing_event, fishing_mode from fishy.engine.semifisher.fishing_event import FishEvent -from fishy.engine.semifisher.fishing_mode import Colors, FishingMode -from fishy.engine.semifisher.pixel_loc import PixelLoc from fishy.helper import helper from fishy.helper.luaparser import sv_color_extract @@ -22,7 +24,7 @@ if typing.TYPE_CHECKING: class SemiFisherEngine(IEngine): def __init__(self, gui_ref: Optional['Callable[[], GUI]']): super().__init__(gui_ref) - self.fishPixWindow = None + self.window = None def run(self): """ @@ -30,34 +32,43 @@ class SemiFisherEngine(IEngine): code explained in comments in detail """ fishing_event.init() - self.fishPixWindow = WindowClient() + self.window = WindowClient(color=cv2.COLOR_RGB2GRAY, show_name="semifisher debug") # check for game window and stuff self.gui.bot_started(True) - sv_color_extract(Colors) - if self.get_gui: logging.info("Starting the bot engine, look at the fishing hole to start fishing") Thread(target=self._wait_and_check).start() - while self.start and WindowClient.running(): - capture = self.fishPixWindow.get_capture() + self.window.crop = get_qr_location(self.window.get_capture()) + if self.window.crop is None: + log_raise("FishyQR not found") + while self.start and WindowClient.running(): + capture = self.window.processed_image(func=image_pre_process) + + # if window server crashed if capture is None: - # if window server crashed self.gui.bot_started(False) self.toggle_start() continue - self.fishPixWindow.crop = PixelLoc.val - fishing_mode.loop(capture[0][0]) + # crop qr and get the values from it + values = get_values_from_image(capture) + if values is None: + self.gui.bot_started(False) + self.toggle_start() + continue + + fishing_mode.loop(values[3]) time.sleep(0.1) + self.window.show(False) logging.info("Fishing engine stopped") self.gui.bot_started(False) fishing_event.unsubscribe() - self.fishPixWindow.destory() + self.window.destory() def _wait_and_check(self): time.sleep(10) @@ -71,7 +82,7 @@ class SemiFisherEngine(IEngine): t = 0 while t < 10.0: t += freq - logging.debug(str(FishingMode.CurrentMode) + ":" + str(self.fishPixWindow.get_capture()[0][0])) + logging.debug(str(FishingMode.CurrentMode) + ":" + str(self.window.get_capture()[0][0])) time.sleep(freq) logging.debug("Will display pixel values for 10 seconds") diff --git a/fishy/engine/semifisher/fishing_event.py b/fishy/engine/semifisher/fishing_event.py index de8b839..8619c88 100644 --- a/fishy/engine/semifisher/fishing_event.py +++ b/fishy/engine/semifisher/fishing_event.py @@ -107,7 +107,10 @@ def fisher_callback(event: State): def on_idle(): - if FishEvent.previousState in (State.FISHING, State.REELIN): + if FishEvent.previousState == State.REELIN: + logging.info("HOLE DEPLETED") + _sound_and_send_fishy_data() + elif FishEvent.previousState == State.FISHING: logging.info("FISHING INTERRUPTED") _sound_and_send_fishy_data() diff --git a/fishy/engine/semifisher/fishing_mode.py b/fishy/engine/semifisher/fishing_mode.py index bf8a05c..ec2c505 100644 --- a/fishy/engine/semifisher/fishing_mode.py +++ b/fishy/engine/semifisher/fishing_mode.py @@ -17,21 +17,6 @@ class State(Enum): DEAD = 15 -Colors = { - State.IDLE : [255, 255, 255], - State.LOOKAWAY : [ 76, 0, 76], - State.LOOKING : [101, 69, 0], - State.DEPLETED : [ 0, 76, 76], - State.NOBAIT : [255, 204, 0], - State.FISHING : [ 75, 156, 213], - State.REELIN : [ 0, 204, 0], - State.LOOT : [ 0, 0, 204], - State.INVFULL : [ 0, 0, 51], - State.FIGHT : [204, 0, 0], - State.DEAD : [ 51, 51, 51] -} - - def _notify(event): for subscriber in subscribers: subscriber(event) @@ -42,17 +27,12 @@ class FishingMode: PrevMode = State.IDLE -def loop(rgb): +def loop(state_num: int): """ Executed in the start of the main loop in fishy.py Changes modes, calls mode events (callbacks) when mode is changed - - :param rgb: rgb read by the bot """ - FishingMode.CurrentMode = State.IDLE - for s in State: - if all(rgb == Colors[s]): - FishingMode.CurrentMode = s + FishingMode.CurrentMode = State(state_num) if FishingMode.CurrentMode != FishingMode.PrevMode: _notify(FishingMode.CurrentMode) diff --git a/fishy/engine/semifisher/pixel_loc.py b/fishy/engine/semifisher/pixel_loc.py deleted file mode 100644 index 90854de..0000000 --- a/fishy/engine/semifisher/pixel_loc.py +++ /dev/null @@ -1,66 +0,0 @@ -import cv2 - - -def get_keypoint_from_image(img): - """ - convert image int hsv - creates a mask for brown color - uses blob detection to find a blob in the mask - filter the blobs to find the correct one - - :param img: rgb image - :return: location of the pixel which is used to detect different fishing states - """ - - # Setup SimpleBlobDetector parameters. - hsv_img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV) - lower = (99, 254, 100) - upper = (100, 255, 101) - mask = cv2.inRange(hsv_img, lower, upper) - - # 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. - key_points = detector.detect(mask) - - if len(key_points) <= 0: - return None - - return int(key_points[0].pt[0]), int(key_points[0].pt[1]) - - -class PixelLoc: - """ - finds the pixel loc and store it - """ - - val = None - - @staticmethod - def config(): - """ - Uses the game window to get an image of the game screen - then uses `GetKeypointFromImage()` to find the Chalutier pixel location - :return: false if pixel loc not found - """ - - PixelLoc.val = (0, 0, 1, 1) - - return True