diff --git a/settings.toml b/settings.toml index d9e17b74008c3c753a63836210baa78f9555fa0d..2410c6a1dce07f9c1892bc237880c37600322cb1 100644 --- a/settings.toml +++ b/settings.toml @@ -1,11 +1,14 @@ [default] CAMERA = 'RASPI_V2' -OBJECTS_DETECTION_MODEL = "ssd_mobilenet_v2_roco_2018_03_29_20200314_015411_TWITCH_TEMP_733_IMGS_29595steps" +OBJECTS_DETECTION_MODEL = "ssd_mobilenet_v2__210224_042137__Twitch2_Train_T470149066_T470150052_T470152289_T470153081_T470158483_1775_imgs__25297_steps" ARMOR_DIGIT_MODEL = "20210117_145856_kd_cnn/distiled - temp 4.1e+01 - cnn - (32) - lr 7.8e-04 - drop 63.pkl" -SERIAL_PORT = "/dev/ttyTHS0" -BAUD_RATE = 230_400 + +USE_UART = false [development] [production] +SERIAL_PORT = "/dev/ttyTHS0" +BAUD_RATE = 230_400 +USE_UART = true \ No newline at end of file diff --git a/src/polystar/communication/command.py b/src/polystar/communication/command.py index f906f84b6b3cdd2f16d3a0336581d67ef7bd2eb7..a494ddd4d779c4f5f09d8b41094d174d07a29a33 100644 --- a/src/polystar/communication/command.py +++ b/src/polystar/communication/command.py @@ -15,3 +15,6 @@ class Command: def make_target_command(target: SimpleTarget) -> Command: return Command(id=2, data=b"Y" + bytes(target)) + + +NO_TARGET_COMMAND = Command(id=2, data=b"N\x00\x00\x00\x00\x00\x00") diff --git a/src/polystar/communication/cs_link_abc.py b/src/polystar/communication/cs_link_abc.py index fd31cf48e0de988fcef5850c14d921d051dea8a2..ad299ea0492c4d2804afd41a03780b6cfa3a77bc 100644 --- a/src/polystar/communication/cs_link_abc.py +++ b/src/polystar/communication/cs_link_abc.py @@ -1,7 +1,8 @@ from abc import ABC, abstractmethod from typing import Iterable -from polystar.communication.command import Command +from polystar.communication.command import NO_TARGET_COMMAND, Command, make_target_command +from polystar.target_pipeline.target_abc import SimpleTarget class CSLinkABC(ABC): @@ -11,3 +12,9 @@ class CSLinkABC(ABC): @abstractmethod def send_command(self, command: Command): pass + + def send_target(self, target: SimpleTarget): + self.send_command(make_target_command(target)) + + def send_no_target(self): + self.send_command(NO_TARGET_COMMAND) diff --git a/src/polystar/communication/togglabe_cs_link.py b/src/polystar/communication/togglabe_cs_link.py new file mode 100644 index 0000000000000000000000000000000000000000..2cc7a6d969919ddad2562f8480b8fd1f1e48a7ba --- /dev/null +++ b/src/polystar/communication/togglabe_cs_link.py @@ -0,0 +1,16 @@ +from polystar.communication.command import Command +from polystar.communication.cs_link_abc import CSLinkABC + + +class TogglableCSLink(CSLinkABC): + def __init__(self, cs_link: CSLinkABC, is_on: bool): + self.is_on = is_on + self.cs_link = cs_link + + def send_command(self, command: Command): + if not self.is_on: + return + self.cs_link.send_command(command) + + def toggle(self): + self.is_on = not self.is_on diff --git a/src/polystar/dependency_injection.py b/src/polystar/dependency_injection.py index ab8bfdf374d9f88b20e76bb86d257b664b8860ea..6cb199adc8d5c980233015f2b9bbedc99e6ca0fe 100644 --- a/src/polystar/dependency_injection.py +++ b/src/polystar/dependency_injection.py @@ -22,7 +22,10 @@ from polystar.target_pipeline.detected_objects.detected_objects_factory import D from polystar.target_pipeline.object_selectors.closest_object_selector import ClosestObjectSelector from polystar.target_pipeline.object_selectors.object_selector_abc import ObjectSelectorABC from polystar.target_pipeline.objects_detectors.objects_detector_abc import ObjectsDetectorABC -from polystar.target_pipeline.objects_filters.confidence_object_filter import ConfidenceObjectsFilter +from polystar.target_pipeline.objects_filters.confidence_object_filter import ( + ConfidenceObjectsFilter, + RobotArmorConfidenceObjectsFilter, +) from polystar.target_pipeline.objects_filters.objects_filter_abc import ObjectsFilterABC from polystar.target_pipeline.objects_linker.objects_linker_abs import ObjectsLinkerABC from polystar.target_pipeline.objects_linker.simple_objects_linker import SimpleObjectsLinker @@ -82,7 +85,7 @@ class CommonModule(Module): @multiprovider @singleton def provide_objects_validators(self) -> List[ObjectsFilterABC]: - return [ConfidenceObjectsFilter(0.6)] + return [RobotArmorConfidenceObjectsFilter(0.5)] @provider @singleton @@ -97,9 +100,9 @@ class CommonModule(Module): @provider @singleton def provide_cs_link(self) -> CSLinkABC: - if self.settings.is_dev: + if not self.settings.USE_UART: return Screen() - return BoardA(Serial(settings.SERIAL_PORT)) + return BoardA(Serial(settings.SERIAL_PORT, settings.BAUD_RATE)) @provider @singleton diff --git a/src/polystar/target_pipeline/detected_objects/detected_robot.py b/src/polystar/target_pipeline/detected_objects/detected_robot.py index 97f72f46611f6477ff02c4c129d2812acc0ea607..13f5c363a136084ea3587cff3994a1c3a617f006 100644 --- a/src/polystar/target_pipeline/detected_objects/detected_robot.py +++ b/src/polystar/target_pipeline/detected_objects/detected_robot.py @@ -13,4 +13,4 @@ class DetectedRobot(DetectedROCOObject): class FakeDetectedRobot(DetectedRobot): def __init__(self, armor: DetectedArmor): - super().__init__(type=ObjectType.CAR, box=armor.box, confidence=0, armors=[armor]) + super().__init__(type=ObjectType.CAR, box=armor.box, confidence=armor.confidence, armors=[armor]) diff --git a/src/polystar/target_pipeline/objects_detectors/trt_model_object_detector.py b/src/polystar/target_pipeline/objects_detectors/trt_model_object_detector.py index 1c406ce70c6cb904f8e7045936d202a8ef129179..4e2c0f42ecdd39601a217c6ef208f04057cc2866 100644 --- a/src/polystar/target_pipeline/objects_detectors/trt_model_object_detector.py +++ b/src/polystar/target_pipeline/objects_detectors/trt_model_object_detector.py @@ -42,7 +42,7 @@ class TRTModelObjectsDetector(ObjectsDetectorABC): object_class_id=int(object_class_id), ) for (_, object_class_id, score, xmin, ymin, xmax, ymax) in results - if object_class_id >= 0 + if object_class_id >= 0 and object_class_id == 4 # FIXME : remove me ], image, ) diff --git a/src/polystar/target_pipeline/objects_filters/confidence_object_filter.py b/src/polystar/target_pipeline/objects_filters/confidence_object_filter.py index 075b5c2fc4ee4c71cbaae5aaef9f257fc6d2bb94..dc1bf9c609c3559af1dee49181ddccb0931064b0 100644 --- a/src/polystar/target_pipeline/objects_filters/confidence_object_filter.py +++ b/src/polystar/target_pipeline/objects_filters/confidence_object_filter.py @@ -1,5 +1,6 @@ from polystar.filters.filter_abc import FilterABC from polystar.target_pipeline.detected_objects.detected_object import DetectedROCOObject +from polystar.target_pipeline.detected_objects.detected_robot import DetectedRobot class ConfidenceObjectsFilter(FilterABC[DetectedROCOObject]): @@ -10,3 +11,11 @@ class ConfidenceObjectsFilter(FilterABC[DetectedROCOObject]): def validate_single(self, obj: DetectedROCOObject) -> bool: return obj.confidence >= self.confidence_threshold + + +class RobotArmorConfidenceObjectsFilter(FilterABC[DetectedRobot]): + def __init__(self, armor_confidence_threshold: float): + self.armor_confidence_threshold = armor_confidence_threshold + + def validate_single(self, robot: DetectedRobot) -> bool: + return any(armor.confidence >= self.armor_confidence_threshold for armor in robot.armors) diff --git a/src/polystar/view/cv2_results_viewer.py b/src/polystar/view/cv2_results_viewer.py index ef93a3d092a97346eba85c9ecb31eaea53f50b35..98594c241c1c625fdb25e9ecc3a0aa360f4e1fc2 100644 --- a/src/polystar/view/cv2_results_viewer.py +++ b/src/polystar/view/cv2_results_viewer.py @@ -1,3 +1,6 @@ +from contextlib import suppress +from typing import Any, Callable, Dict + import cv2 import numpy as np @@ -23,10 +26,12 @@ COLORS = [ (23, 190, 207), ] # seaborn.color_palette() * 255 +Callback = Callable[[], Any] + class CV2ResultViewer(ResultViewerABC): - def __init__(self, name: str, delay: int = 1, end_keys: str = "q"): - self.end_keys = [ord(c) for c in end_keys] + def __init__(self, name: str, delay: int = 1, end_key: str = "q", key_callbacks: Dict[str, Callback] = None): + self.keycode_callbacks = self._make_keycode_callbacks(end_key, key_callbacks or {}) self.delay = delay self.name = name self._current_image: Image = None @@ -70,4 +75,13 @@ class CV2ResultViewer(ResultViewerABC): def display(self): cv2.imshow(self.name, self._current_image) - self.finished = cv2.waitKey(self.delay) & 0xFF in self.end_keys + keycode = cv2.waitKey(self.delay) & 0xFF + with suppress(KeyError): + self.keycode_callbacks[keycode]() + + def stop(self): + self.finished = True + + def _make_keycode_callbacks(self, end_key: str, key_callbacks: Dict[str, Callback]) -> Dict[int, Callback]: + key_callbacks[end_key] = self.stop + return {ord(k): f for k, f in key_callbacks.items()} diff --git a/src/research/armors/demos/__init__.py b/src/research/armors/demos/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/src/research/armors/demos/demo_pipeline_camera.py b/src/research/armors/demos/demo_pipeline_camera.py deleted file mode 100644 index 113e5e41c7da3dee0397baed6fc68b6848f0a76a..0000000000000000000000000000000000000000 --- a/src/research/armors/demos/demo_pipeline_camera.py +++ /dev/null @@ -1,31 +0,0 @@ -from injector import inject - -from polystar.dependency_injection import make_injector -from polystar.frame_generators.frames_generator_abc import FrameGeneratorABC -from polystar.target_pipeline.debug_pipeline import DebugTargetPipeline -from polystar.target_pipeline.target_pipeline import NoTargetFoundException -from polystar.utils.fps import FPS -from polystar.view.cv2_results_viewer import CV2ResultViewer - - -@inject -def demo_pipeline_on_camera(pipeline: DebugTargetPipeline, webcam: FrameGeneratorABC): - fps, pipeline_fps = FPS(), FPS() - with CV2ResultViewer("TensorRT demo") as viewer: - for image in webcam.generate(): - pipeline_fps.skip() - try: - pipeline.predict_target(image) - except NoTargetFoundException: - pass - pipeline_fps.tick(), fps.tick() - viewer.add_debug_info(pipeline.debug_info_) - viewer.add_text(f"FPS: {fps:.1f} / {pipeline_fps:.1f}", 10, 10, (0, 0, 0)) - viewer.display() - fps.skip() - if viewer.finished: - return - - -if __name__ == "__main__": - make_injector().call_with_injection(demo_pipeline_on_camera) diff --git a/src/research/armors/demos/demo_pipeline.py b/src/research/scripts/demo_pipeline.py similarity index 100% rename from src/research/armors/demos/demo_pipeline.py rename to src/research/scripts/demo_pipeline.py diff --git a/src/research/scripts/demo_pipeline_camera.py b/src/research/scripts/demo_pipeline_camera.py new file mode 100644 index 0000000000000000000000000000000000000000..c2c79fd0c205ecca1e929a9ca05d54e4a9e8da9f --- /dev/null +++ b/src/research/scripts/demo_pipeline_camera.py @@ -0,0 +1,53 @@ +from injector import inject + +from polystar.communication.cs_link_abc import CSLinkABC +from polystar.communication.togglabe_cs_link import TogglableCSLink +from polystar.dependency_injection import make_injector +from polystar.frame_generators.frames_generator_abc import FrameGeneratorABC +from polystar.models.image import Image +from polystar.target_pipeline.debug_pipeline import DebugTargetPipeline +from polystar.target_pipeline.target_pipeline import NoTargetFoundException +from polystar.utils.fps import FPS +from polystar.view.cv2_results_viewer import CV2ResultViewer + + +class CameraPipelineDemo: + @inject + def __init__(self, pipeline: DebugTargetPipeline, webcam: FrameGeneratorABC, cs_link: CSLinkABC): + self.cs_link = TogglableCSLink(cs_link, is_on=False) + self.webcam = webcam + self.pipeline = pipeline + self.fps, self.pipeline_fps = FPS(), FPS() + self.persistence_last_detection = 0 + + def run(self): + with CV2ResultViewer("TensorRT demo", key_callbacks={" ": self.cs_link.toggle}) as viewer: + for image in self.webcam.generate(): + self.pipeline_fps.skip() + self._detect(image) + self.pipeline_fps.tick(), self.fps.tick() + self._display(viewer) + self.fps.skip() + if viewer.finished: + return + + def _detect(self, image: Image): + try: + target = self.pipeline.predict_target(image) + self.cs_link.send_target(target) + self.persistence_last_detection = 5 + except NoTargetFoundException: + if self.persistence_last_detection: + self.persistence_last_detection -= 1 + else: + self.cs_link.send_no_target() + + def _display(self, viewer: CV2ResultViewer): + viewer.add_debug_info(self.pipeline.debug_info_) + viewer.add_text(f"FPS: {self.fps:.1f} / {self.pipeline_fps:.1f}", 10, 10, (0, 0, 0)) + viewer.add_text("Communication: " + ("[ON]" if self.cs_link.is_on else "[OFF]"), 10, 30, (0, 0, 0)) + viewer.display() + + +if __name__ == "__main__": + make_injector().get(CameraPipelineDemo).run()