diff --git a/detection/api/mpf_component_api/mpf_component_api.py b/detection/api/mpf_component_api/mpf_component_api.py index 1b9d96a..162375a 100644 --- a/detection/api/mpf_component_api/mpf_component_api.py +++ b/detection/api/mpf_component_api/mpf_component_api.py @@ -26,7 +26,7 @@ import dataclasses import enum -from typing import Any, Dict, Mapping, NamedTuple, Optional +from typing import Any, Dict, List, Mapping, NamedTuple, Optional @dataclasses.dataclass @@ -73,6 +73,16 @@ class VideoJob(NamedTuple): feed_forward_track: Optional[VideoTrack] = None +class AllVideoTracksJob(NamedTuple): + job_name: str + data_uri: str + start_frame: int + stop_frame: int + job_properties: Mapping[str, str] + media_properties: Mapping[str, str] + feed_forward_tracks: List[VideoTrack] + + class ImageJob(NamedTuple): job_name: str data_uri: str diff --git a/detection/examples/PythonOcvComponent/ocv_component/ocv_component.py b/detection/examples/PythonOcvComponent/ocv_component/ocv_component.py index da3c704..0eb495e 100644 --- a/detection/examples/PythonOcvComponent/ocv_component/ocv_component.py +++ b/detection/examples/PythonOcvComponent/ocv_component/ocv_component.py @@ -41,14 +41,13 @@ def get_detections_from_image_reader( image_job: mpf.ImageJob, image_reader: mpf_util.ImageReader) -> Iterable[mpf.ImageLocation]: - logger.info('[%s] Received image job: %s', image_job.job_name, image_job) + logger.info('Received image job: %s', image_job) model = get_model(image_job) # A real component would use the model. img = image_reader.get_image() height, width, _ = img.shape - logger.info('[%s] Image at %s: width = %s, height = %s', - image_job.job_name, image_job.data_uri, width, height) + logger.info('Image at %s: width = %s, height = %s', image_job.data_uri, width, height) detection_sz = 20 yield mpf.ImageLocation(width // 2 - detection_sz, 0, detection_sz, height - 1, -1.0, @@ -62,7 +61,7 @@ def get_detections_from_video_capture( self, video_job: mpf.VideoJob, video_capture: mpf_util.VideoCapture) -> Iterable[mpf.VideoTrack]: - logger.info('[%s] Received video job: %s', video_job.job_name, video_job) + logger.info('Received video job: %s', video_job) model = get_model(video_job) # A real component would use the model. width, height = video_capture.frame_size @@ -96,9 +95,9 @@ def get_model(job): models_dir_path = os.path.join(job.job_properties.get('MODELS_DIR_PATH', '.'), 'PythonOcvComponent') model_settings = ModelSettings(model_name, models_dir_path) - logger.info('[%s] Successfully retrieved settings file for the "%s" model: ' + logger.info('Successfully retrieved settings file for the "%s" model: ' '{ network = "%s", names = "%s", num_classes = %s }', - job.job_name, model_name, model_settings.network, model_settings.names, model_settings.num_classes) + model_name, model_settings.network, model_settings.names, model_settings.num_classes) return load_model(model_settings) diff --git a/detection/examples/PythonTestComponent/Dockerfile b/detection/examples/PythonTestComponent/Dockerfile new file mode 100644 index 0000000..5709793 --- /dev/null +++ b/detection/examples/PythonTestComponent/Dockerfile @@ -0,0 +1,34 @@ +# syntax=docker/dockerfile:1.2 + +############################################################################# +# NOTICE # +# # +# This software (or technical data) was produced for the U.S. Government # +# under contract, and is subject to the Rights in Data-General Clause # +# 52.227-14, Alt. IV (DEC 2007). # +# # +# Copyright 2024 The MITRE Corporation. All Rights Reserved. # +############################################################################# + +############################################################################# +# Copyright 2024 The MITRE Corporation # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################# + +ARG BUILD_REGISTRY +ARG BUILD_TAG=latest +FROM ${BUILD_REGISTRY}openmpf_python_executor_ssb:${BUILD_TAG} + +RUN --mount=target=.,readwrite \ + install-component.sh diff --git a/detection/examples/PythonTestComponent/descriptor/descriptor.json b/detection/examples/PythonTestComponent/plugin-files/descriptor/descriptor.json similarity index 95% rename from detection/examples/PythonTestComponent/descriptor/descriptor.json rename to detection/examples/PythonTestComponent/plugin-files/descriptor/descriptor.json index 37fa664..f852f1b 100644 --- a/detection/examples/PythonTestComponent/descriptor/descriptor.json +++ b/detection/examples/PythonTestComponent/plugin-files/descriptor/descriptor.json @@ -3,7 +3,7 @@ "componentVersion": "9.0", "middlewareVersion": "9.0", "sourceLanguage": "python", - "batchLibrary": "${MPF_HOME}/plugins/PythonTestComponent/test_component.py", + "batchLibrary": "PythonTestComponent", "environmentVariables": [], "algorithm": { "name": "PYTHONTEST", diff --git a/detection/examples/PythonTestComponent/pyproject.toml b/detection/examples/PythonTestComponent/pyproject.toml new file mode 100644 index 0000000..bcd2b65 --- /dev/null +++ b/detection/examples/PythonTestComponent/pyproject.toml @@ -0,0 +1,29 @@ +############################################################################# +# NOTICE # +# # +# This software (or technical data) was produced for the U.S. Government # +# under contract, and is subject to the Rights in Data-General Clause # +# 52.227-14, Alt. IV (DEC 2007). # +# # +# Copyright 2024 The MITRE Corporation. All Rights Reserved. # +############################################################################# + +############################################################################# +# Copyright 2024 The MITRE Corporation # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################# + +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" diff --git a/detection/examples/PythonTestComponent/setup.cfg b/detection/examples/PythonTestComponent/setup.cfg new file mode 100644 index 0000000..49f1634 --- /dev/null +++ b/detection/examples/PythonTestComponent/setup.cfg @@ -0,0 +1,39 @@ +############################################################################# +# NOTICE # +# # +# This software (or technical data) was produced for the U.S. Government # +# under contract, and is subject to the Rights in Data-General Clause # +# 52.227-14, Alt. IV (DEC 2007). # +# # +# Copyright 2024 The MITRE Corporation. All Rights Reserved. # +############################################################################# + +############################################################################# +# Copyright 2024 The MITRE Corporation # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################# + +[metadata] +name = PythonTestComponent +version = 9.0 + +[options] +packages = test_component +install_requires = + mpf_component_api>=9.0 + mpf_component_util>=9.0 + +[options.entry_points] +mpf.exported_component = + component = test_component.test_component:TestComponent diff --git a/detection/examples/PythonTestComponent/test_component/__init__.py b/detection/examples/PythonTestComponent/test_component/__init__.py new file mode 100644 index 0000000..d43e3fd --- /dev/null +++ b/detection/examples/PythonTestComponent/test_component/__init__.py @@ -0,0 +1,27 @@ +############################################################################# +# NOTICE # +# # +# This software (or technical data) was produced for the U.S. Government # +# under contract, and is subject to the Rights in Data-General Clause # +# 52.227-14, Alt. IV (DEC 2007). # +# # +# Copyright 2024 The MITRE Corporation. All Rights Reserved. # +############################################################################# + +############################################################################# +# Copyright 2024 The MITRE Corporation # +# # +# Licensed under the Apache License, Version 2.0 (the "License"); # +# you may not use this file except in compliance with the License. # +# You may obtain a copy of the License at # +# # +# http://www.apache.org/licenses/LICENSE-2.0 # +# # +# Unless required by applicable law or agreed to in writing, software # +# distributed under the License is distributed on an "AS IS" BASIS, # +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +# See the License for the specific language governing permissions and # +# limitations under the License. # +############################################################################# + +from .test_component import TestComponent diff --git a/detection/examples/PythonTestComponent/test_component.py b/detection/examples/PythonTestComponent/test_component/test_component.py similarity index 84% rename from detection/examples/PythonTestComponent/test_component.py rename to detection/examples/PythonTestComponent/test_component/test_component.py index 2149ff1..6099617 100644 --- a/detection/examples/PythonTestComponent/test_component.py +++ b/detection/examples/PythonTestComponent/test_component/test_component.py @@ -25,13 +25,12 @@ ############################################################################# import logging +import uuid import mpf_component_api as mpf - logger = logging.getLogger('TestComponent') - class TestComponent(object): def __init__(self): @@ -40,7 +39,7 @@ def __init__(self): @staticmethod # Make sure executor can call static methods def get_detections_from_image(image_job): - logger.info('[%s] Received image job: %s', image_job.job_name, image_job) + logger.info('Received image job: %s', image_job) if image_job.feed_forward_location is not None: yield image_job.feed_forward_location return @@ -63,14 +62,17 @@ def get_detections_from_image(image_job): 'ECHO_JOB': echo_job, 'ECHO_MEDIA': echo_media}) - logger.info('[%s] Found %s detections', image_job.job_name, 2) - + logger.info('Found %s detections', 2) # Doesn't need to be a instance method, just making sure executor can call instance methods def get_detections_from_video(self, video_job): - logger.info('[%s] Received video job: %s', video_job.job_name, video_job) + logger.info('Received video job: %s', video_job) + random_uuid = uuid.uuid4() + if video_job.feed_forward_track is not None: + video_job.feed_forward_track.detection_properties['annotated_prop_single_track'] = 'annotated_val_single_track' + video_job.feed_forward_track.detection_properties['uuid_single_track'] = random_uuid return [video_job.feed_forward_track] echo_job, echo_media = self.get_echo_msgs(video_job) @@ -94,10 +96,23 @@ def get_detections_from_video(self, video_job): return [track1, track2] + # Doesn't need to be a instance method, just making sure executor can call instance methods + def get_detections_from_all_video_tracks(self, video_job): + logger.info('Received all tracks video job: %s', video_job) + random_uuid = uuid.uuid4() + + if video_job.feed_forward_tracks is not None: + for feed_forward_track in video_job.feed_forward_tracks: + feed_forward_track.detection_properties['annotated_prop_all_tracks'] = 'annotated_val_all_tracks' + feed_forward_track.detection_properties['uuid_all_tracks'] = random_uuid + return video_job.feed_forward_tracks + + return [] + @classmethod # Doesn't need to be a class method, just making sure executor can call class methods def get_detections_from_audio(cls, audio_job): - logger.info('[%s] Received audio job: %s', audio_job.job_name, audio_job) + logger.info('Received audio job: %s', audio_job) if audio_job.feed_forward_track is not None: return audio_job.feed_forward_track, echo_job, echo_media = cls.get_echo_msgs(audio_job) @@ -107,6 +122,7 @@ def get_detections_from_audio(cls, audio_job): # Make sure multiple return values are accepted return track1, mpf.AudioTrack(10, 20, 1, detection_properties) + @staticmethod def get_echo_msgs(job): # Make sure properties get converted between C++ and Python properly