From 06e49d0de49da32ea71232d58bc84b27db895de1 Mon Sep 17 00:00:00 2001 From: Brian Rosenberg Date: Thu, 9 Feb 2023 15:09:04 -0500 Subject: [PATCH 1/7] Remove TriggeredJobConfig --- .../mpf_component_util/__init__.py | 2 +- .../mpf_component_util/job_config.py | 74 +++++-------------- 2 files changed, 20 insertions(+), 56 deletions(-) diff --git a/detection/component_util/mpf_component_util/__init__.py b/detection/component_util/mpf_component_util/__init__.py index d4d1023..ca2e8e5 100644 --- a/detection/component_util/mpf_component_util/__init__.py +++ b/detection/component_util/mpf_component_util/__init__.py @@ -40,5 +40,5 @@ from .http_retry import HttpRetry from .job_config import ( - TriggerMismatch, NoInBoundsSpeechSegments, TriggeredJobConfig, DynamicSpeechJobConfig, SpeakerInfo + TriggerMismatch, NoInBoundsSpeechSegments, DynamicSpeechJobConfig, SpeakerInfo ) diff --git a/detection/component_util/mpf_component_util/job_config.py b/detection/component_util/mpf_component_util/job_config.py index 181456f..4a13590 100644 --- a/detection/component_util/mpf_component_util/job_config.py +++ b/detection/component_util/mpf_component_util/job_config.py @@ -65,53 +65,6 @@ def __str__(self): f"job start time, {self.n_late} after job end time)." ) -class TriggeredJobConfig(object): - """ Handles job parsing logic - - :ivar job_name: Job name - :ivar target_file: File location of input data - :ivar is_triggered_job: Whether job contains a feed-forward track - """ - def __init__(self, job: MpfJob): - # General job data - self.job_name: str - self.target_file: Path - self.is_triggered_job: bool = False - self._add_job_data(job) - - if self.is_triggered_job: - self._check_trigger(job) - - # Job properties - self._add_job_properties(job.job_properties) - - def _add_job_data(self, job: MpfJob): - self.target_file = Path(job.data_uri) - self.job_name = job.job_name - if job.feed_forward_track is not None: - self.is_triggered_job = 'TRIGGER' in job.job_properties - - @staticmethod - def _check_trigger(job: MpfJob): - # Check TRIGGER is met - trigger = mpf_util.get_property( - properties=job.job_properties, - key='TRIGGER', - default_value='', - prop_type=str - ) - - t_key, t_val = trigger.strip().split('=') - - # If trigger key is in feed-forward properties, only run - # transcription if the value matches trigger val - if t_key not in job.feed_forward_track.detection_properties: - raise TriggerMismatch(t_key, t_val) - if job.feed_forward_track.detection_properties[t_key] != t_val: - raise TriggerMismatch(t_key, t_val, job.feed_forward_track.detection_properties[t_key]) - - def _add_job_properties(self, job_properties: Mapping[str, str]): - raise NotImplementedError() class SpeakerInfo(NamedTuple): @@ -123,10 +76,13 @@ class SpeakerInfo(NamedTuple): speech_segs: List[Any] -class DynamicSpeechJobConfig(TriggeredJobConfig): +class DynamicSpeechJobConfig: """ Handles job parsing logic for components that may be part of a dynamic speech pipeline + :ivar job_name: Job name + :ivar target_file: File location of input data + :ivar is_triggered_job: Whether job contains a feed-forward track :ivar start_time: Start time of the audio (in milliseconds) :ivar stop_time: Stop time of the audio (in milliseconds) :ivar fps: Frames per second for video jobs @@ -139,12 +95,18 @@ class DynamicSpeechJobConfig(TriggeredJobConfig): if feed-forward track exists, otherwise None """ def __init__(self, job: MpfSpeechJob): + self.job_name = job.job_name + self.target_file = Path(job.data_uri) + self.is_triggered_job = (job.feed_forward_track is not None + and bool(job.job_properties.get('TRIGGER'))) self.start_time: int self.stop_time: Optional[int] = None self.fps: Optional[float] = None self.speaker_id_prefix: str self.overwrite_ids: bool - super().__init__(job) + + self._add_media_info(job) + self._add_job_properties(job.job_properties) # Properties related to dynamic speech pipelines self.speaker: Optional[SpeakerInfo] = None @@ -152,8 +114,12 @@ def __init__(self, job: MpfSpeechJob): if self.is_triggered_job: self._add_feed_forward_properties(job) - def _add_job_data(self, job: MpfSpeechJob): - super()._add_job_data(job) + + def _add_job_properties(self, job_properties: Mapping[str, str]): + raise NotImplementedError() + + + def _add_media_info(self, job: MpfSpeechJob): self.overwrite_ids = self.is_triggered_job media_duration = float(job.media_properties.get('DURATION', -1)) @@ -298,15 +264,13 @@ def _add_feed_forward_properties(self, job: MpfSpeechJob): speech_segs=speech_segs ) - def _add_job_properties(self, job_properties: Mapping[str, str]): - raise NotImplementedError() @staticmethod def _parse_segments_str( segments_string: str, - media_start_time: Optional[int] = 0, + media_start_time: int = 0, media_stop_time: Optional[int] = None - ) -> Optional[List[Tuple[int, int]]]: + ) -> List[Tuple[int, int]]: """ Converts a string of the form 'start_1-stop_1, start_2-stop_2, ..., start_n-stop_n' where start_x and stop_x are in milliseconds, to a list of int pairs From ed285716ea3da5bde104a387c470aa9e01b51f7a Mon Sep 17 00:00:00 2001 From: Brian Rosenberg Date: Mon, 10 Jul 2023 11:27:36 -0400 Subject: [PATCH 2/7] Update DynamicSpeechJobConfig --- .../mpf_component_util/job_config.py | 29 ++----------------- 1 file changed, 2 insertions(+), 27 deletions(-) diff --git a/detection/component_util/mpf_component_util/job_config.py b/detection/component_util/mpf_component_util/job_config.py index f8ccdf9..c0c3b6d 100644 --- a/detection/component_util/mpf_component_util/job_config.py +++ b/detection/component_util/mpf_component_util/job_config.py @@ -32,28 +32,9 @@ import mpf_component_util as mpf_util -MpfJob = Union[mpf.AudioJob, mpf.VideoJob, mpf.ImageJob, mpf.GenericJob] MpfSpeechJob = Union[mpf.AudioJob, mpf.VideoJob] -class TriggerMismatch(Exception): - """ Exception raised when the feed-forward track does not meet the trigger. - - :param trigger_key: The trigger key defined by TRIGGER property - :param expected: The expected value defined by TRIGGER property - :param trigger_val: The actual value of the trigger property (if present) - """ - def __init__(self, trigger_key: str, expected: str, trigger_val: Optional[str]=None): - self.trigger_key = trigger_key - self.expected = expected - self.trigger_val = trigger_val - - def __str__(self): - if self.trigger_val is None: - return f"Trigger property {self.trigger_key} not present in feed-forward detection properties" - return f"Expected {self.trigger_key} to be {self.expected}, got {self.trigger_val}" - - class NoInBoundsSpeechSegments(Exception): def __init__(self, n_early: int, n_late: int): self.n_early = n_early @@ -66,7 +47,6 @@ def __str__(self): ) - class SpeakerInfo(NamedTuple): speaker_id: str gender: str @@ -82,7 +62,6 @@ class DynamicSpeechJobConfig: :ivar job_name: Job name :ivar target_file: File location of input data - :ivar is_triggered_job: Whether job contains a feed-forward track :ivar start_time: Start time of the audio (in milliseconds) :ivar stop_time: Stop time of the audio (in milliseconds) :ivar fps: Frames per second for video jobs @@ -96,13 +75,10 @@ class DynamicSpeechJobConfig: def __init__(self, job: MpfSpeechJob): self.job_name = job.job_name self.target_file = Path(job.data_uri) - self.is_triggered_job = (job.feed_forward_track is not None - and bool(job.job_properties.get('TRIGGER'))) self.start_time: int self.stop_time: Optional[int] = None self.fps: Optional[float] = None self.speaker_id_prefix: str - self.overwrite_ids: bool self._add_media_info(job) self._add_job_properties(job.job_properties) @@ -110,7 +86,8 @@ def __init__(self, job: MpfSpeechJob): # Properties related to dynamic speech pipelines self.speaker: Optional[SpeakerInfo] = None self.override_default_language: Optional[str] = None - if self.is_triggered_job: + if (job.feed_forward_track and + 'VOICED_SEGMENTS' in job.feed_forward_track.detection_properties): self._add_feed_forward_properties(job) @@ -119,8 +96,6 @@ def _add_job_properties(self, job_properties: Mapping[str, str]): def _add_media_info(self, job: MpfSpeechJob): - self.overwrite_ids = self.is_triggered_job - media_duration = float(job.media_properties.get('DURATION', -1)) if isinstance(job, mpf.VideoJob): start_frame = job.start_frame From 742c4c0049e3070632d7dba9bd665413d294c537 Mon Sep 17 00:00:00 2001 From: Brian Rosenberg Date: Mon, 10 Jul 2023 11:31:26 -0400 Subject: [PATCH 3/7] Change LONG_SPEAKER_ID to SPEAKER_ID --- detection/component_util/mpf_component_util/job_config.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/detection/component_util/mpf_component_util/job_config.py b/detection/component_util/mpf_component_util/job_config.py index c0c3b6d..76d7749 100644 --- a/detection/component_util/mpf_component_util/job_config.py +++ b/detection/component_util/mpf_component_util/job_config.py @@ -65,7 +65,7 @@ class DynamicSpeechJobConfig: :ivar start_time: Start time of the audio (in milliseconds) :ivar stop_time: Stop time of the audio (in milliseconds) :ivar fps: Frames per second for video jobs - :ivar speaker_id_prefix: Prefix for LONG_SPEAKER_ID + :ivar speaker_id_prefix: Prefix for SPEAKER_ID :ivar override_default_language: A default ISO 639-3 language to use when languages defined in the speaker are not supported. If the feed-forward track does not exist, this is None @@ -135,11 +135,12 @@ def _add_media_info(self, job: MpfSpeechJob): self.stop_time = None self.speaker_id_prefix = f"{self.start_time}-{self.stop_time or 'EOF'}-" + def _add_feed_forward_properties(self, job: MpfSpeechJob): feed_forward_properties = job.feed_forward_track.detection_properties speaker_id = mpf_util.get_property( properties=feed_forward_properties, - key='LONG_SPEAKER_ID', + key='SPEAKER_ID', default_value='0-EOF-1', prop_type=str ) From 7f0484f9d70116f560c26abcd68ad8b0f5b7d2f7 Mon Sep 17 00:00:00 2001 From: Brian Rosenberg Date: Mon, 10 Jul 2023 12:49:31 -0400 Subject: [PATCH 4/7] fix test --- detection/component_util/mpf_component_util/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detection/component_util/mpf_component_util/__init__.py b/detection/component_util/mpf_component_util/__init__.py index 68591c4..7174337 100644 --- a/detection/component_util/mpf_component_util/__init__.py +++ b/detection/component_util/mpf_component_util/__init__.py @@ -40,5 +40,5 @@ from .http_retry import HttpRetry from .job_config import ( - TriggerMismatch, NoInBoundsSpeechSegments, DynamicSpeechJobConfig, SpeakerInfo + NoInBoundsSpeechSegments, DynamicSpeechJobConfig, SpeakerInfo ) From 03e3de1a087284fecaad21655a32b2566988e605 Mon Sep 17 00:00:00 2001 From: Brian Rosenberg Date: Tue, 11 Jul 2023 11:04:13 -0400 Subject: [PATCH 5/7] Make gender fields Optional --- .../mpf_component_util/job_config.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/detection/component_util/mpf_component_util/job_config.py b/detection/component_util/mpf_component_util/job_config.py index 76d7749..8e44e57 100644 --- a/detection/component_util/mpf_component_util/job_config.py +++ b/detection/component_util/mpf_component_util/job_config.py @@ -26,12 +26,11 @@ from collections import defaultdict from pathlib import Path -from typing import Union, Optional, Mapping, List, Dict, Tuple, NamedTuple, Any +from typing import Dict, List, Mapping, NamedTuple, Optional, Tuple, Union import mpf_component_api as mpf import mpf_component_util as mpf_util - MpfSpeechJob = Union[mpf.AudioJob, mpf.VideoJob] @@ -49,11 +48,11 @@ def __str__(self): class SpeakerInfo(NamedTuple): speaker_id: str - gender: str - gender_score: float language: str language_scores: Dict[str, float] - speech_segs: List[Any] + speech_segs: List[Tuple[int, int]] + gender: Optional[str] = None + gender_score: Optional[float] = None class DynamicSpeechJobConfig: @@ -148,14 +147,14 @@ def _add_feed_forward_properties(self, job: MpfSpeechJob): gender = mpf_util.get_property( properties=feed_forward_properties, key='GENDER', - default_value='', + default_value=None, prop_type=str ) gender_score = mpf_util.get_property( properties=feed_forward_properties, key='GENDER_CONFIDENCE', - default_value=-1.0, + default_value=None, prop_type=float ) @@ -212,9 +211,9 @@ def _add_feed_forward_properties(self, job: MpfSpeechJob): speaker_id=speaker_id, language=language, language_scores=language_scores, + speech_segs=speech_segs, gender=gender, - gender_score=gender_score, - speech_segs=speech_segs + gender_score=gender_score ) From 3ac9947f8f2bc2fbabd0511e167b9424f9c1733e Mon Sep 17 00:00:00 2001 From: Brian Rosenberg Date: Mon, 28 Aug 2023 12:08:45 -0400 Subject: [PATCH 6/7] Remove detection_type --- .../examples/PythonOcvComponent/ocv_component/ocv_component.py | 2 -- detection/examples/PythonTestComponent/test_component.py | 1 - 2 files changed, 3 deletions(-) diff --git a/detection/examples/PythonOcvComponent/ocv_component/ocv_component.py b/detection/examples/PythonOcvComponent/ocv_component/ocv_component.py index 61b8eda..98288be 100644 --- a/detection/examples/PythonOcvComponent/ocv_component/ocv_component.py +++ b/detection/examples/PythonOcvComponent/ocv_component/ocv_component.py @@ -35,8 +35,6 @@ logger = logging.getLogger('OcvComponent') class OcvComponent(mpf_util.ImageReaderMixin, mpf_util.VideoCaptureMixin): - detection_type = 'TEST OCV DETECTION TYPE' - def get_detections_from_image_reader( self, diff --git a/detection/examples/PythonTestComponent/test_component.py b/detection/examples/PythonTestComponent/test_component.py index aad50b5..56d97a3 100644 --- a/detection/examples/PythonTestComponent/test_component.py +++ b/detection/examples/PythonTestComponent/test_component.py @@ -33,7 +33,6 @@ class TestComponent(object): - detection_type = 'TEST DETECTION TYPE' def __init__(self): logger.info('Creating instance of TestComponent') From cc97a3c185220843499d83d23138af883a26b324 Mon Sep 17 00:00:00 2001 From: Brian Rosenberg Date: Tue, 5 Sep 2023 08:23:39 -0400 Subject: [PATCH 7/7] Add trackType to example descriptors --- .../PythonOcvComponent/plugin-files/descriptor/descriptor.json | 1 + .../examples/PythonTestComponent/descriptor/descriptor.json | 1 + 2 files changed, 2 insertions(+) diff --git a/detection/examples/PythonOcvComponent/plugin-files/descriptor/descriptor.json b/detection/examples/PythonOcvComponent/plugin-files/descriptor/descriptor.json index eec0b62..924ed04 100644 --- a/detection/examples/PythonOcvComponent/plugin-files/descriptor/descriptor.json +++ b/detection/examples/PythonOcvComponent/plugin-files/descriptor/descriptor.json @@ -9,6 +9,7 @@ "name": "PYTHONOCVTEST", "description": "A dummy Python detection component.", "actionType": "DETECTION", + "trackType": "TEST OCV TRACK TYPE", "outputChangedCounter" : 1, "requiresCollection": { "states": [] diff --git a/detection/examples/PythonTestComponent/descriptor/descriptor.json b/detection/examples/PythonTestComponent/descriptor/descriptor.json index 7335ac3..da73d8e 100644 --- a/detection/examples/PythonTestComponent/descriptor/descriptor.json +++ b/detection/examples/PythonTestComponent/descriptor/descriptor.json @@ -9,6 +9,7 @@ "name": "PYTHONTEST", "description": "A dummy Python detection component.", "actionType": "DETECTION", + "trackType": "TEST TRACK TYPE", "outputChangedCounter" : 1, "requiresCollection": { "states": []