6 Commits

7 changed files with 1420 additions and 1461 deletions

View File

@ -1,4 +1,4 @@
FROM docker.io/library/python:3.10-slim as base FROM docker.io/library/python:3.11-slim as base
# Configure piwheels repo to use pre-compiled numpy wheels for arm # Configure piwheels repo to use pre-compiled numpy wheels for arm
RUN echo -n "[global]\n" > /etc/pip.conf &&\ RUN echo -n "[global]\n" > /etc/pip.conf &&\
@ -19,7 +19,7 @@ RUN blobconverter --zoo-name mobile_object_localizer_192x192 --zoo-type depthai
FROM base as builder FROM base as builder
RUN apt-get install -y git && \ RUN apt-get install -y git && \
pip3 install poetry==1.2.0 && \ pip3 install poetry && \
poetry self add "poetry-dynamic-versioning[plugin]" poetry self add "poetry-dynamic-versioning[plugin]"
ADD poetry.lock . ADD poetry.lock .
@ -35,6 +35,8 @@ RUN poetry build
################# #################
FROM base FROM base
COPY camera_tunning /camera_tuning
RUN mkdir /models RUN mkdir /models
COPY --from=model-builder /models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob /models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob COPY --from=model-builder /models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob /models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob

View File

@ -10,7 +10,11 @@ import typing, types
import depthai as dai import depthai as dai
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
from . import depthai as cam # pylint: disable=reimported from camera import oak_pipeline as cam
CAMERA_EXPOSITION_DEFAULT = "default"
CAMERA_EXPOSITION_8300US = "8300us"
CAMERA_EXPOSITION_500US = "500us"
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -45,6 +49,14 @@ def _parse_args_cli() -> argparse.Namespace:
help="threshold to filter detected objects", help="threshold to filter detected objects",
type=float, type=float,
default=_get_env_float_value("OBJECTS_THRESHOLD", 0.2)) default=_get_env_float_value("OBJECTS_THRESHOLD", 0.2))
parser.add_argument("-f", "--camera-fps",
help="set rate at which camera should produce frames",
type=int,
default=30)
parser.add_argument("--camera-tuning-exposition", type=str,
default=CAMERA_EXPOSITION_DEFAULT,
help="override camera exposition configuration",
choices=[CAMERA_EXPOSITION_DEFAULT, CAMERA_EXPOSITION_500US, CAMERA_EXPOSITION_8300US])
parser.add_argument("-H", "--image-height", help="image height", parser.add_argument("-H", "--image-height", help="image height",
type=int, type=int,
default=_get_env_int_value("IMAGE_HEIGHT", 120)) default=_get_env_int_value("IMAGE_HEIGHT", 120))
@ -94,6 +106,12 @@ def execute_from_command_line() -> None:
objects_threshold=args.objects_threshold) objects_threshold=args.objects_threshold)
pipeline = dai.Pipeline() pipeline = dai.Pipeline()
if args.camera_tuning_exposition == CAMERA_EXPOSITION_500US:
pipeline.setCameraTuningBlobPath('/camera_tuning/tuning_exp_limit_500us.bin')
elif args.camera_tuning_exposition == CAMERA_EXPOSITION_8300US:
pipeline.setCameraTuningBlobPath('/camera_tuning/tuning_exp_limit_8300us.bin')
pipeline_controller = cam.PipelineController(pipeline=pipeline, pipeline_controller = cam.PipelineController(pipeline=pipeline,
frame_processor=frame_processor, frame_processor=frame_processor,
object_processor=object_processor, object_processor=object_processor,
@ -101,6 +119,7 @@ def execute_from_command_line() -> None:
camera=cam.CameraSource(pipeline=pipeline, camera=cam.CameraSource(pipeline=pipeline,
img_width=args.image_width, img_width=args.image_width,
img_height=args.image_height, img_height=args.image_height,
fps=args.camera_fps,
)) ))
def sigterm_handler(signum: int, frame: typing.Optional[ def sigterm_handler(signum: int, frame: typing.Optional[

View File

@ -5,6 +5,7 @@ import abc
import datetime import datetime
import logging import logging
import pathlib import pathlib
import time
import typing import typing
from dataclasses import dataclass from dataclasses import dataclass
@ -22,6 +23,9 @@ _NN_PATH = "/models/mobile_object_localizer_192x192_openvino_2021.4_6shave.blob"
_NN_WIDTH = 192 _NN_WIDTH = 192
_NN_HEIGHT = 192 _NN_HEIGHT = 192
_PREVIEW_WIDTH = 640
_PREVIEW_HEIGHT = 480
class ObjectProcessor: class ObjectProcessor:
""" """
@ -196,20 +200,22 @@ class ObjectDetectionNN:
class CameraSource(Source): class CameraSource(Source):
"""Image source based on camera preview""" """Image source based on camera preview"""
def __init__(self, pipeline: dai.Pipeline, img_width: int, img_height: int): def __init__(self, pipeline: dai.Pipeline, img_width: int, img_height: int, fps: int):
self._cam_rgb = pipeline.createColorCamera() self._cam_rgb = pipeline.createColorCamera()
self._xout_rgb = pipeline.createXLinkOut() self._xout_rgb = pipeline.createXLinkOut()
self._xout_rgb.setStreamName("rgb") self._xout_rgb.setStreamName("rgb")
# Properties # Properties
self._cam_rgb.setBoardSocket(dai.CameraBoardSocket.RGB) self._cam_rgb.setBoardSocket(dai.CameraBoardSocket.RGB)
self._cam_rgb.setPreviewSize(width=img_width, height=img_height) self._cam_rgb.setPreviewSize(width=_PREVIEW_WIDTH, height=_PREVIEW_HEIGHT)
self._cam_rgb.setInterleaved(False) self._cam_rgb.setInterleaved(False)
self._cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.RGB) self._cam_rgb.setColorOrder(dai.ColorCameraProperties.ColorOrder.RGB)
self._cam_rgb.setFps(30) self._cam_rgb.setFps(fps)
self._resize_manip = self._configure_manip(pipeline=pipeline, img_width=img_width, img_height=img_height)
# link camera preview to output # link camera preview to output
self._cam_rgb.preview.link(self._xout_rgb.input) self._cam_rgb.preview.link(self._resize_manip.inputImage)
self._resize_manip.out.link(self._xout_rgb.input)
def link(self, input_node: dai.Node.Input) -> None: def link(self, input_node: dai.Node.Input) -> None:
self._cam_rgb.preview.link(input_node) self._cam_rgb.preview.link(input_node)
@ -217,6 +223,15 @@ class CameraSource(Source):
def get_stream_name(self) -> str: def get_stream_name(self) -> str:
return self._xout_rgb.getStreamName() return self._xout_rgb.getStreamName()
@staticmethod
def _configure_manip(pipeline: dai.Pipeline, img_width: int, img_height: int) -> dai.node.ImageManip:
# Resize image
manip = pipeline.createImageManip()
manip.initialConfig.setResize(img_width, img_height)
manip.initialConfig.setFrameType(dai.ImgFrame.Type.RGB888p)
manip.initialConfig.setKeepAspectRatio(False)
return manip
@dataclass @dataclass
class MqttConfig: class MqttConfig:
@ -330,6 +345,10 @@ class PipelineController:
q_nn = dev.getOutputQueue(name=self._object_node.get_stream_name(), maxSize=queue_size, # type: ignore q_nn = dev.getOutputQueue(name=self._object_node.get_stream_name(), maxSize=queue_size, # type: ignore
blocking=False) blocking=False)
start_time = time.time()
counter = 0
fps = 0
display_time = time.time()
self._stop = False self._stop = False
while True: while True:
if self._stop: if self._stop:
@ -341,6 +360,16 @@ class PipelineController:
except Exception as ex: except Exception as ex:
logger.exception("unexpected error: %s", str(ex)) logger.exception("unexpected error: %s", str(ex))
counter += 1
if (time.time() - start_time) > 1:
fps = counter / (time.time() - start_time)
counter = 0
start_time = time.time()
if (time.time() - display_time) >= 10:
display_time = time.time()
logger.info("fps: %s", fps)
def _loop_on_camera_events(self, q_nn: dai.DataOutputQueue, q_rgb: dai.DataOutputQueue) -> None: def _loop_on_camera_events(self, q_nn: dai.DataOutputQueue, q_rgb: dai.DataOutputQueue) -> None:
logger.debug("wait for new frame") logger.debug("wait for new frame")
@ -361,6 +390,7 @@ class PipelineController:
self._object_processor.process(in_nn, frame_ref) self._object_processor.process(in_nn, frame_ref)
logger.debug("objects processed") logger.debug("objects processed")
def stop(self) -> None: def stop(self) -> None:
""" """
Stop event loop, if loop is not running, do nothing Stop event loop, if loop is not running, do nothing

Binary file not shown.

Binary file not shown.

2806
poetry.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -9,15 +9,14 @@ packages = [
] ]
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.10" python = "^3.11"
paho-mqtt = "^1.6.1" paho-mqtt = "^1.6.1"
depthai = "^2.19.0" depthai = "^2.22.0"
protobuf3 = "^0.2.1" protobuf3 = "^0.2.1"
google = "^3.0.0" google = "^3.0.0"
blobconverter = "^1.3.0"
protobuf = "^4.21.8" protobuf = "^4.21.8"
opencv-python-headless = "^4.6.0.66" opencv-python-headless = "^4.6.0.66"
robocar-protobuf = {version = "^1.1.2", source = "robocar"} robocar-protobuf = {version = "^1.3.0", source = "robocar"}
[tool.poetry.group.test.dependencies] [tool.poetry.group.test.dependencies]
@ -35,8 +34,7 @@ types-protobuf = "^3.20.4.2"
[[tool.poetry.source]] [[tool.poetry.source]]
name = "robocar" name = "robocar"
url = "https://git.cyrilix.bzh/api/packages/robocars/pypi/simple" url = "https://git.cyrilix.bzh/api/packages/robocars/pypi/simple"
default = false priority = "explicit"
secondary = false
[build-system] [build-system]
requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"] requires = ["poetry-core>=1.0.0", "poetry-dynamic-versioning"]