From a19688f82457f8d3b299ae77364245091c87cf6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20Sp=C3=B6rk?= Date: Sun, 27 Oct 2024 16:19:04 +0100 Subject: [PATCH] refactoring(data): Refactoring inter-process data passing WIP --- foosball/detectors/color.py | 1 + foosball/hooks/__init__.py | 132 +++++++++++--------- foosball/models.py | 37 ++---- foosball/pipe/BaseProcess.py | 10 +- foosball/source/__init__.py | 3 +- foosball/source/gear.py | 2 +- foosball/source/opencv.py | 2 +- foosball/tracking/__init__.py | 3 +- foosball/tracking/ai.py | 11 +- foosball/tracking/analyzer/ScoreAnalyzer.py | 42 +++++-- foosball/tracking/analyzer/__init__.py | 18 ++- foosball/tracking/analyzer/analyze.py | 30 ++--- foosball/tracking/effects/__init__.py | 8 -- foosball/tracking/preprocess.py | 7 +- foosball/tracking/render.py | 19 +-- foosball/tracking/tracker.py | 13 +- tests/it/__init__.py | 2 +- tests/it/it_test.py | 4 +- 18 files changed, 166 insertions(+), 178 deletions(-) delete mode 100644 foosball/tracking/effects/__init__.py diff --git a/foosball/detectors/color.py b/foosball/detectors/color.py index f731fa1..9d9faa1 100644 --- a/foosball/detectors/color.py +++ b/foosball/detectors/color.py @@ -134,6 +134,7 @@ def detect(self, frame) -> DetectedBall: return DetectedBall(ball=detected_blob, frame=detection_frame) else: logger.error("Ball Detection not possible. Config is None") + return DetectedBall(ball=None, frame=None) class GoalColorDetector(GoalDetector[GoalColorConfig]): diff --git a/foosball/hooks/__init__.py b/foosball/hooks/__init__.py index 7831efc..b7f3f49 100644 --- a/foosball/hooks/__init__.py +++ b/foosball/hooks/__init__.py @@ -4,70 +4,82 @@ import random import threading import traceback -from typing import Mapping, Callable +from abc import ABC, abstractmethod +from typing import Mapping, Self import urllib3 import yaml -from foosball.models import Team - logger = logging.getLogger(__name__) -@dataclasses.dataclass -class Webhook: - method: str - url: str - json: dict = None - headers: Mapping[str, str] = None - - -def load_goal_webhook() -> Callable[[Team], Webhook]: - filename = 'goal_webhook.yaml' - - def to_webhook(webhook_dict: dict, team: Team): - webhook_dict['json']['team'] = team.value - return Webhook(**webhook_dict) - if os.path.isfile(filename): - with open(filename, 'r') as f: - wh = yaml.safe_load(f) - return lambda team: to_webhook(wh, team) - else: - logger.info("No goal webhook configured under 'goal_webhook.yaml'") - - -generate_goal_webhook = load_goal_webhook() - - -def webhook(whook: Webhook): - threading.Thread( - target=_webhook, - args=[whook], - daemon=True - ).start() - - -def _webhook(whook: Webhook): - try: - headers = {} if whook.headers is None else whook.headers - if whook.json is not None and "content-type" not in headers: - headers['content_type'] = 'application/json' - response = urllib3.request(whook.method, whook.url, json=whook.json, headers=headers) - logger.debug(f"webhook response: {response.status}") - except Exception as e: - logger.error(f"Webhook failed - {e}") - traceback.print_exc() - - -def play_random_sound(folder: str, prefix: str = './assets/audio'): - path = f'{prefix}/{folder}' - audio_file = random.choice(os.listdir(path)) - play_sound(f"{path}/{audio_file}") - - -def play_sound(sound_file: str): - from playsound import playsound - if os.path.isfile(sound_file): - playsound(sound_file, block=False) - else: - logger.warning(f"Audio not found: {sound_file}") +class Hook(ABC): + @abstractmethod + def invoke(self, *args, **kwargs): + pass + + +class AudioHook(Hook): + + def __init__(self, folder: str): + super().__init__() + self.folder = folder + + def invoke(self, *args, **kwargs): + AudioHook.play_random_sound(self.folder) + + @staticmethod + def play_sound(sound_file: str): + from playsound import playsound + if os.path.isfile(sound_file): + playsound(sound_file, block=False) + else: + logger.warning(f"Audio not found: {sound_file}") + + @staticmethod + def play_random_sound(folder: str, prefix: str = './assets/audio'): + path = f'{prefix}/{folder}' + audio_file = random.choice(os.listdir(path)) + AudioHook.play_sound(f"{path}/{audio_file}") + + +class Webhook(Hook): + def __init__(self, method: str = None, url: str = None, json: dict = None, headers: Mapping[str, str] = None, *args, **kwargs): + self.method: str = method + self.url: str = url + self.json: dict = json + self.headers: Mapping[str, str] = headers + + def as_dict(self, json: dict = None) -> dict: + d = vars(self) + if json is not None: + d['json'] = d['json'] | json + return d + + def invoke(self, json: dict, *args, **kwargs): + threading.Thread( + target=Webhook.call, + kwargs=self.as_dict(json), + daemon=True + ).start() + + @staticmethod + def call(method: str, url: str, json: dict = None, headers: Mapping[str, str] = None): + try: + headers = {} if headers is None else headers + if json is not None and "content-type" not in headers: + headers['content_type'] = 'application/json' + response = urllib3.request(method, url, json=json, headers=headers) + logger.debug(f"webhook response: {response.status}") + except Exception as e: + logger.error(f"Webhook failed - {e}") + traceback.print_exc() + + @classmethod + def load_webhook(cls, filename: str) -> Self: + if os.path.isfile(filename): + with open(filename, 'r') as f: + wh = yaml.safe_load(f) + return Webhook(**wh) + else: + logger.info("No goal webhook configured under 'goal_webhook.yaml'") diff --git a/foosball/models.py b/foosball/models.py index 0093b3f..96eefae 100644 --- a/foosball/models.py +++ b/foosball/models.py @@ -1,8 +1,7 @@ import collections from dataclasses import dataclass -from typing import TypeVar from enum import Enum -from typing import Optional, Union, Generic +from typing import Optional, Union import cv2 import numpy as np @@ -79,7 +78,7 @@ def area(self): @dataclass class DetectedBall: - ball: Blob + ball: Optional[Blob] frame: np.array @@ -138,32 +137,22 @@ def to_string(self): return " - ".join([i.to_string() for i in self.infos]) -R = TypeVar('R') - - @dataclass -class Result(Generic[R]): - info: InfoLog - data: R +class TrackerResult: + frame: Frame + goals: Goals | None + ball_track: Track | None + ball: Blob | None @dataclass -class TrackerResultData: - frame: CPUFrame - goals: Optional[Goals] - ball_track: Track - ball: Blob - - -TrackerResult = Result[TrackerResultData] - - -@dataclass -class PreprocessorResultData: - original: CPUFrame - preprocessed: Optional[CPUFrame] +class PreprocessorResult: + original: Frame + preprocessed: Optional[Frame] homography_matrix: Optional[np.ndarray] # 3x3 matrix used to warp the image and project points goals: Optional[Goals] -PreprocessorResult = Result[PreprocessorResultData] +@dataclass +class RendererResult: + frame: Optional[Frame] diff --git a/foosball/pipe/BaseProcess.py b/foosball/pipe/BaseProcess.py index df5f860..1d71a46 100644 --- a/foosball/pipe/BaseProcess.py +++ b/foosball/pipe/BaseProcess.py @@ -1,12 +1,12 @@ import abc +import datetime as dt import logging import multiprocessing import traceback -import datetime as dt -from queue import Empty, Full from dataclasses import dataclass +from queue import Empty, Full -from foosball.models import InfoLog, Result, R +from foosball.models import InfoLog from foosball.pipe.Pipe import clear, SENTINEL @@ -25,11 +25,9 @@ def add(self, name: str, data: any, info=InfoLog([])): else: self.info = InfoLog([]) - def remove(self, name) -> Result[R]: + def remove(self, name) -> any: return self.kwargs.pop(name) - - def __init__(self, args=None, kwargs=None, timestamp=dt.datetime.now()): if kwargs is None: kwargs = dict() diff --git a/foosball/source/__init__.py b/foosball/source/__init__.py index 1e8b8b7..43674f5 100644 --- a/foosball/source/__init__.py +++ b/foosball/source/__init__.py @@ -4,6 +4,7 @@ from multiprocessing import Queue from queue import Full from threading import Thread +from typing import Tuple from foosball.models import Frame from foosball.pipe.BaseProcess import Msg @@ -88,7 +89,7 @@ def close_capture(self) -> None: pass @abstractmethod - def dim(self) -> [int, int]: + def dim(self) -> Tuple[int, int]: pass def stop(self): diff --git a/foosball/source/gear.py b/foosball/source/gear.py index 57dfa87..8f4202d 100644 --- a/foosball/source/gear.py +++ b/foosball/source/gear.py @@ -47,4 +47,4 @@ def dim(self): width = int(self.gear.stream.stream.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(self.gear.stream.stream.get(cv2.CAP_PROP_FRAME_HEIGHT)) - return [width, height] + return tuple((width, height)) diff --git a/foosball/source/opencv.py b/foosball/source/opencv.py index ae8e3fd..c6a213c 100644 --- a/foosball/source/opencv.py +++ b/foosball/source/opencv.py @@ -24,4 +24,4 @@ def dim(self): width = int(self.cap.get(cv2.CAP_PROP_FRAME_WIDTH)) height = int(self.cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) - return [width, height] + return tuple((width, height)) diff --git a/foosball/tracking/__init__.py b/foosball/tracking/__init__.py index 66bace6..dc418cd 100644 --- a/foosball/tracking/__init__.py +++ b/foosball/tracking/__init__.py @@ -33,8 +33,7 @@ class Tracking: def __init__(self, stream, dims: FrameDimensions, goal_detector: GoalDetector, ball_detector: BallDetector, headless=False, maxPipeSize=128, calibrationMode=None, goalGracePeriod=1.0, **kwargs): super().__init__() self.calibrationMode = calibrationMode - - width, height = dims.scaled + (width, height) = dims.scaled mask = generate_frame_mask(width, height) gpu_flags = kwargs.get(GPU) self.preprocessor = PreProcessor(dims, goal_detector, mask=mask, headless=headless, useGPU='preprocess' in gpu_flags, diff --git a/foosball/tracking/ai.py b/foosball/tracking/ai.py index d126537..e8851ec 100644 --- a/foosball/tracking/ai.py +++ b/foosball/tracking/ai.py @@ -1,6 +1,7 @@ import logging import traceback from queue import Empty +from typing import Tuple from imutils.video import FPS from vidgear.gears import WriteGear @@ -35,8 +36,7 @@ def __init__(self, source: Source, dis, *args, **kwargs): original = self.source.dim() self.scale = kwargs.get(SCALE) scaled = self.scale_dim(original, self.scale) - self.dims = FrameDimensions(original, scaled, self.scale) - + self.dims = FrameDimensions(original=original, scaled=scaled, scale=self.scale) self.goal_detector = GoalColorDetector(GoalColorConfig.preset()) self.ball_detector = BallColorDetector(BallColorConfig.preset(kwargs.get(BALL))) @@ -106,7 +106,8 @@ def step_frame(): self.logger.debug("received SENTINEL") break self.fps.update() - frame = msg.kwargs.get('Renderer', None) + result = msg.kwargs.get('Renderer', None) + frame = result.frame if result is not None else None info: InfoLog = msg.info self.fps.stop() fps = int(self.fps.fps()) @@ -167,9 +168,9 @@ def adjust_calibration(self): self.tracking.config_input(self.calibration.config) @staticmethod - def scale_dim(dim, scale_percent): + def scale_dim(dim, scale_percent) -> Tuple[int, int]: # calculate the percent of original dimensions width = int(dim[0] * scale_percent) height = int(dim[1] * scale_percent) - return [width, height] + return tuple((width, height)) diff --git a/foosball/tracking/analyzer/ScoreAnalyzer.py b/foosball/tracking/analyzer/ScoreAnalyzer.py index 1f8521a..40dd0bf 100644 --- a/foosball/tracking/analyzer/ScoreAnalyzer.py +++ b/foosball/tracking/analyzer/ScoreAnalyzer.py @@ -1,18 +1,27 @@ +import datetime as dt import multiprocessing import traceback +from dataclasses import dataclass from typing import Optional -import datetime as dt -from . import AbstractAnalyzer, ScoreAnalyzerResult, ScoreAnalyzerResultData -from ...models import Team, Goals, Score, Track, Info, Verbosity, TrackerResult, InfoLog +from . import AbstractAnalyzer +from ...hooks import AudioHook, Webhook +from ...models import Team, Goals, Score, Track, Info, Verbosity, InfoLog +from ...pipe.BaseProcess import Msg from ...utils import contains +@dataclass +class ScoreAnalyzerResult: + score: Score + team_scored: Team + + class ScoreAnalyzer(AbstractAnalyzer): def close(self): pass - def __init__(self, goal_grace_period_sec: float = 1.0, *args, **kwargs): + def __init__(self, goal_grace_period_sec: float = 1.0, audio: bool = False, webhook: bool = False, *args, **kwargs): super().__init__(name="ScoreAnalyzer") self.kwargs = kwargs self.goal_grace_period_sec = goal_grace_period_sec @@ -21,6 +30,7 @@ def __init__(self, goal_grace_period_sec: float = 1.0, *args, **kwargs): self.last_track_sighting: dt.datetime | None = None self.last_track: Optional[Track] = None self.goal_candidate = None + self.hooks = [AudioHook("goal"), Webhook.load_webhook('goal_webhook.yaml')] @staticmethod def is_track_empty(track: Track): @@ -42,9 +52,9 @@ def goal_shot(self, goals: Goals, track: Track) -> Optional[Team]: self.logger.error(f"Error {e}") return None - def analyze(self, track_result: TrackerResult, timestamp: dt.datetime) -> ScoreAnalyzerResult: - goals = track_result.data.goals - track = track_result.data.ball_track + def analyze(self, msg: Msg, timestamp: dt.datetime) -> [ScoreAnalyzerResult, InfoLog]: + goals = msg.kwargs["Tracker"].goals + track = msg.kwargs["Tracker"].ball_track info = InfoLog([]) team_scored = None try: @@ -61,7 +71,7 @@ def analyze(self, track_result: TrackerResult, timestamp: dt.datetime) -> ScoreA # let's wait for track (s.a.), or we run out of grace period (down below) # whatever happens first if self.goal_candidate is not None and self.last_track_sighting is not None and no_track_sighting_in_grace_period: - self.count_goal(self.goal_candidate) + self.count_goal(self.goal_candidate, timestamp) team_scored = self.goal_candidate self.goal_candidate = None elif self.goal_candidate is None: @@ -72,15 +82,23 @@ def analyze(self, track_result: TrackerResult, timestamp: dt.datetime) -> ScoreA traceback.print_exc() self.last_track = track info.append(Info(verbosity=Verbosity.INFO, title="Score", value=self.score.to_string())) - return ScoreAnalyzerResult(data=ScoreAnalyzerResultData(score=self.score, team_scored=team_scored), info=info) + return [ScoreAnalyzerResult(score=self.score, team_scored=team_scored), info] def check_reset_score(self): if self.score_reset.is_set(): self.score.reset() self.score_reset.clear() - def count_goal(self, team: Team): + def count_goal(self, team: Team, timestamp: dt.datetime): self.score.inc(team) + for h in self.hooks: + try: + h.invoke({"team": team.value}, timestamp) + except Exception as e: + self.logger.error("Error in Analyzer - effects ") + print(e) + print(type(h)) + traceback.print_exc() - def reset_score(self): - self.score_reset.set() \ No newline at end of file + def reset(self): + self.score_reset.set() diff --git a/foosball/tracking/analyzer/__init__.py b/foosball/tracking/analyzer/__init__.py index 31f71ce..1ab2a3f 100644 --- a/foosball/tracking/analyzer/__init__.py +++ b/foosball/tracking/analyzer/__init__.py @@ -1,17 +1,9 @@ import logging from abc import ABC, abstractmethod -from dataclasses import dataclass from datetime import datetime -from foosball.models import TrackerResult, Score, Team, Result - -@dataclass -class ScoreAnalyzerResultData: - score: Score - team_scored: Team - - -ScoreAnalyzerResult = Result[ScoreAnalyzerResultData] +from foosball.models import InfoLog +from foosball.pipe.BaseProcess import Msg class AbstractAnalyzer(ABC): @@ -21,5 +13,9 @@ def __init__(self, name: str = "UnknownAnalyzer", **kwargs): self.logger = logging.getLogger(name) @abstractmethod - def analyze(self, track_result: TrackerResult, timestamp: datetime) -> dict: + def analyze(self, msg: Msg, timestamp: datetime) -> [dict, InfoLog]: + pass + + @abstractmethod + def reset(self): pass diff --git a/foosball/tracking/analyzer/analyze.py b/foosball/tracking/analyzer/analyze.py index d46b8e3..52546b0 100644 --- a/foosball/tracking/analyzer/analyze.py +++ b/foosball/tracking/analyzer/analyze.py @@ -2,8 +2,7 @@ from .ScoreAnalyzer import ScoreAnalyzer from ... import hooks -from ...hooks import generate_goal_webhook -from ...models import Team, TrackerResult +from ...models import Team from ...pipe.BaseProcess import BaseProcess, Msg @@ -14,34 +13,21 @@ def close(self): def __init__(self, audio: bool = False, webhook: bool = False, goal_grace_period_sec: float = 1.0, *args, **kwargs): super().__init__(name="Analyzer") self.kwargs = kwargs - self.analyzers = [ScoreAnalyzer(goal_grace_period_sec, args, kwargs)] - # TODO: catch up here - self.effects = [] + self.analyzers = [ScoreAnalyzer(goal_grace_period_sec, audio, webhook, args, kwargs)] self.audio = audio self.webhook = webhook - def call_hooks(self, team: Team) -> None: - if self.audio: - hooks.play_random_sound('goal') - if self.webhook: - hooks.webhook(generate_goal_webhook(team)) + def reset_score(self): + for a in self.analyzers: + if type(a) is ScoreAnalyzer: + a.reset() def process(self, msg: Msg) -> Msg: - track_result: TrackerResult = msg.kwargs['Tracker'] - data = track_result.data - for a in self.analyzers: try: - ar = a.analyze(track_result, msg.timestamp) - msg.add(a.name, ar, info=ar.info) + [result, info] = a.analyze(msg, msg.timestamp) + msg.add(a.name, result, info=info) except Exception as e: self.logger.error("Error in Analyzer - analyzers ", e) traceback.print_exc() - # TODO: catch up here - for e in self.effects: - try: - e.invoke(track_result, msg.timestamp) - except Exception as e: - self.logger.error("Error in Analyzer - effects ", e) - traceback.print_exc() return msg diff --git a/foosball/tracking/effects/__init__.py b/foosball/tracking/effects/__init__.py deleted file mode 100644 index 0f12f29..0000000 --- a/foosball/tracking/effects/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ -from abc import abstractmethod, ABC - - -class Effect(ABC): - - @abstractmethod - def invoke(self, *args, **kwargs): - pass diff --git a/foosball/tracking/preprocess.py b/foosball/tracking/preprocess.py index ad2c698..b1a420f 100644 --- a/foosball/tracking/preprocess.py +++ b/foosball/tracking/preprocess.py @@ -11,7 +11,7 @@ from ..arUcos.models import Aruco from ..detectors.color import GoalDetector, GoalColorConfig from ..models import Frame, PreprocessorResult, Point, Rect, Blob, Goals, FrameDimensions, ScaleDirection, \ - InfoLog, Info, Verbosity, PreprocessorResultData + InfoLog, Info, Verbosity from ..pipe.BaseProcess import BaseProcess, Msg from ..pipe.Pipe import clear from ..utils import ensure_cpu, generate_processor_switches, relative_change, scale @@ -147,10 +147,7 @@ def process(self, msg: Msg) -> Msg: except Exception as e: self.logger.error(f"Error in preprocess {e}") traceback.print_exc() - msg.add("Preprocessor", PreprocessorResult( - data=PreprocessorResultData(self.iproc(frame), self.iproc(preprocessed), self.homography_matrix, self.goals), - info=info - )) + msg.add("Preprocessor", PreprocessorResult(original=self.iproc(frame), preprocessed=self.iproc(preprocessed), homography_matrix=self.homography_matrix, goals=self.goals), info=info) return msg @staticmethod diff --git a/foosball/tracking/render.py b/foosball/tracking/render.py index b09acc3..8dde5fc 100644 --- a/foosball/tracking/render.py +++ b/foosball/tracking/render.py @@ -5,7 +5,7 @@ import numpy as np from const import INFO_VERBOSITY -from ..models import Goal, Score, FrameDimensions, Blob, InfoLog, Verbosity +from ..models import Goal, Score, FrameDimensions, Blob, InfoLog, Verbosity, RendererResult from ..pipe.BaseProcess import Msg, BaseProcess from ..utils import generate_processor_switches logger = logging.getLogger(__name__) @@ -98,6 +98,7 @@ def r_track(frame, ball_track, scale) -> None: thickness = max(1, int(int(np.sqrt(ball_track.maxlen / float(i + 1)) * 2) * scale)) cv2.line(frame, ball_track[i - 1], ball_track[i], (b, g, r), thickness) + class Renderer(BaseProcess): def close(self): pass @@ -110,17 +111,17 @@ def __init__(self, dims: FrameDimensions, headless=False, useGPU: bool = False, [self.proc, self.iproc] = generate_processor_switches(useGPU) def process(self, msg: Msg) -> Msg: - goal_analyzer = msg.kwargs["ScoreAnalyzer"] + score_analyzer = msg.kwargs["ScoreAnalyzer"] tracker = msg.kwargs["Tracker"] info: InfoLog = msg.info try: if not self.headless: - shape = tracker.data.frame.shape - f = self.proc(tracker.data.frame) - ball = tracker.data.ball - goals = tracker.data.goals - track = tracker.data.ball_track - score = goal_analyzer.data.score + shape = tracker.frame.shape + f = self.proc(tracker.frame) + ball = tracker.ball + goals = tracker.goals + track = tracker.ball_track + score = score_analyzer.score if ball is not None: r_ball(f, ball) @@ -131,7 +132,7 @@ def process(self, msg: Msg) -> Msg: r_score(f, score, text_scale=1, thickness=4) if self.infoVerbosity is not None: r_info(f, shape, info.filter(self.infoVerbosity), text_scale=0.5, thickness=1) - msg.add("Renderer", self.iproc(f), info=InfoLog([])) + msg.add("Renderer", RendererResult(frame=self.iproc(f)), info=InfoLog([])) except Exception as e: logger.error(f"Error in renderer {e}") traceback.print_exc() diff --git a/foosball/tracking/tracker.py b/foosball/tracking/tracker.py index 72ce917..75d7043 100644 --- a/foosball/tracking/tracker.py +++ b/foosball/tracking/tracker.py @@ -6,17 +6,13 @@ from const import CalibrationMode from .preprocess import WarpMode, project_blob from ..detectors.color import BallColorDetector, BallColorConfig -from ..models import TrackerResult, Track, Info, Blob, Goals, InfoLog, Verbosity, TrackerResultData +from ..models import TrackerResult, Track, Info, Blob, Goals, InfoLog, Verbosity from ..pipe.BaseProcess import BaseProcess, Msg from ..pipe.Pipe import clear from ..utils import generate_processor_switches logger = logging.getLogger(__name__) -def log(result: TrackerResult) -> None: - logger.debug(result.info) - - class Tracker(BaseProcess): def __init__(self, ball_detector: BallColorDetector, useGPU: bool = False, buffer=16, off=False, verbose=False, @@ -74,11 +70,10 @@ def config_input(self, config: BallColorConfig) -> None: def process(self, msg: Msg) -> Msg: preprocess_result = msg.kwargs['Preprocessor'] - data = preprocess_result.data + data = preprocess_result ball = None goals = data.goals ball_track = None - info = None tracker_info = InfoLog([]) try: if not self.off: @@ -109,7 +104,7 @@ def process(self, msg: Msg) -> Msg: traceback.print_exc() # TODO: splitting into Preprocess and Tracker data maybe renders this obsolete if not self.verbose: - msg.add("Tracker", TrackerResult(data=TrackerResultData(data.original, goals, ball_track, ball), info=tracker_info)) + msg.add("Tracker", TrackerResult(frame=data.original, goals=goals, ball_track=ball_track, ball=ball), info=tracker_info) else: - msg.add("Tracker", TrackerResult(data=TrackerResultData(data.preprocessed, goals, ball_track, ball), info=tracker_info)) + msg.add("Tracker", TrackerResult(frame=data.preprocessed, goals=goals, ball_track=ball_track, ball=ball), info=tracker_info) return msg diff --git a/tests/it/__init__.py b/tests/it/__init__.py index 7287995..648d34b 100644 --- a/tests/it/__init__.py +++ b/tests/it/__init__.py @@ -2,4 +2,4 @@ import sys # necessary for pytest-cov to measure coverage myPath = os.path.dirname(os.path.abspath(__file__)) -sys.path.insert(0, myPath + '/../../') \ No newline at end of file +sys.path.insert(0, myPath + '/../../') diff --git a/tests/it/it_test.py b/tests/it/it_test.py index 13adc4d..7680b1b 100644 --- a/tests/it/it_test.py +++ b/tests/it/it_test.py @@ -20,8 +20,10 @@ main_args = { "webhook": True, + "audio": False, "headless": True, - "ball": "yellow" + "ball": "yellow", + "goalGracePeriod": 0.0 }