Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
25a68c2
feat: add new Camera factory
robgee86 Oct 16, 2025
9b6087c
refactor: IPCamera
robgee86 Oct 20, 2025
5d72852
refactor: streamer example
robgee86 Oct 20, 2025
a895c44
perf
robgee86 Oct 20, 2025
fc5f098
refactor: BaseCamera
robgee86 Oct 21, 2025
cf38532
refactor
robgee86 Oct 21, 2025
dd3e533
refactor: image manipulation
robgee86 Oct 24, 2025
13d529b
fix: pipelining
robgee86 Oct 27, 2025
7a9e45c
refactor: remove adjust/adjusted functions
robgee86 Oct 27, 2025
2a4bf60
feat: better support BGR, BGRA and uint8, uint16, uint32
robgee86 Oct 28, 2025
2f8df8c
refactor: deprecate USBCamera in favor of Camera and make it compatible
robgee86 Oct 29, 2025
c645390
doc: update docstrings
robgee86 Oct 29, 2025
7e74fe3
tidy-up
robgee86 Oct 29, 2025
1aa331d
refactor: clearer APIs and doc
robgee86 Oct 29, 2025
954d1e3
add examples
robgee86 Oct 29, 2025
fc1f26b
refactor: directly use V4LCamera
robgee86 Oct 29, 2025
16e9ae7
remove test examples
robgee86 Oct 29, 2025
321eed2
fix: race condition
robgee86 Oct 29, 2025
dd7dc93
doc: update readme
robgee86 Oct 29, 2025
1dc4725
refactor
robgee86 Oct 29, 2025
4ef3eed
doc: explain adjustments argument
robgee86 Oct 29, 2025
563a711
run fmt
robgee86 Oct 30, 2025
f965fa4
run linter
robgee86 Oct 30, 2025
d3a57eb
fix: wrong import
robgee86 Oct 30, 2025
0d3bee1
perf
robgee86 Oct 30, 2025
01b3e41
fix: numeric issue
robgee86 Oct 30, 2025
325c2a7
refactor: change default image serialization format
robgee86 Oct 30, 2025
1fc5dec
perf: reduce buffer size to lower latency
robgee86 Oct 30, 2025
19e7129
doc: better clarify supported image formats
robgee86 Oct 31, 2025
836d69f
feat: allow also image formats with higher bit depth and preserve all…
robgee86 Oct 31, 2025
6fa846f
chore: run fmt
robgee86 Oct 31, 2025
297ad64
refactor: Camera args
robgee86 Oct 31, 2025
564dc7b
perf: make resize a no-op if frame has already target size
robgee86 Nov 3, 2025
12d53d6
refactor
robgee86 Nov 4, 2025
f3e6578
feat: update EI container to add TCP streaming mode
robgee86 Nov 4, 2025
1c91f62
refactor: migrate camera_code_detection
robgee86 Nov 4, 2025
af81594
refactor: migrate vide_objectdetection
robgee86 Nov 4, 2025
cbe4780
refactor: migrate video_objectdetection
robgee86 Nov 4, 2025
7badfe2
refactor
robgee86 Nov 6, 2025
8205c90
fix
robgee86 Nov 10, 2025
13e2c8d
refactors
robgee86 Nov 13, 2025
54f44cb
add tests dependency
robgee86 Nov 13, 2025
8cc5b95
test: add tests for camera module
robgee86 Nov 13, 2025
2475041
fixes
robgee86 Nov 13, 2025
2caf0d4
fix: API
robgee86 Nov 13, 2025
075fdfb
fix: linting
robgee86 Nov 13, 2025
d8f7ed0
chore: bump EI base image version
robgee86 Nov 13, 2025
0f6d472
fix: missing job permissions
robgee86 Nov 13, 2025
6315cb6
fix: port must be an int
robgee86 Nov 13, 2025
6712ed2
feat: add automatic reconnection
robgee86 Nov 14, 2025
b32dea6
feat: resolve device to stable links for more reliable reconnections
robgee86 Nov 15, 2025
29543a9
refactor
robgee86 Nov 15, 2025
e109993
fix: test race condition
robgee86 Nov 15, 2025
28abd60
return exception when capturing if camera has not been started
robgee86 Nov 15, 2025
0fc25c1
refactor
robgee86 Nov 15, 2025
4e8f4df
refactor
robgee86 Nov 15, 2025
be25866
fix tests
robgee86 Nov 15, 2025
0376553
fix: split frame capture and forwarding
robgee86 Nov 16, 2025
8254389
Revert "fix: split frame capture and forwarding"
robgee86 Nov 16, 2025
9fdd4b6
fix: race condition
robgee86 Nov 16, 2025
22093d8
refactor
robgee86 Nov 16, 2025
16f6890
feat: allow to subscribe to camera lifecycle events
robgee86 Nov 18, 2025
6ab8f14
feat: add support for 'paused' and 'resumed' events
robgee86 Nov 18, 2025
bfcf1f3
refactor
robgee86 Nov 18, 2025
5371cac
fix: linting
robgee86 Nov 18, 2025
a8d7ea6
feat: implement a (simple) state machine for state management
robgee86 Nov 18, 2025
8c0ed74
fix
robgee86 Nov 18, 2025
50b762c
refactor: remove host from WebSocketCamera
robgee86 Nov 18, 2025
92ed63a
refactor: rename address -> url
robgee86 Nov 18, 2025
aae5e5b
validate frame_format
robgee86 Nov 18, 2025
6f2e436
fix signature
robgee86 Nov 19, 2025
2588761
feat: add recording to camera
robgee86 Nov 20, 2025
6926b8f
fix: fmt
robgee86 Nov 20, 2025
68a5dba
feat: add support to authentication and encryption
robgee86 Nov 22, 2025
07025df
chore: update EI container version
robgee86 Nov 22, 2025
222b902
feat: add utilities for flipping images
robgee86 Nov 23, 2025
d36da02
feat: change protocol implementation
robgee86 Nov 26, 2025
ad2688f
fix: prime the EI pipeline at startup
robgee86 Nov 26, 2025
fc88f19
refactor: better error logging
robgee86 Nov 26, 2025
89970d3
fix: linting
robgee86 Nov 26, 2025
98d41df
chore: update license header
robgee86 Nov 26, 2025
d8256a1
fix: imports
robgee86 Nov 26, 2025
994bc6a
refactor: align websocket camera with other peripheral implementations
robgee86 Nov 27, 2025
0b33587
feat: add TLS support
robgee86 Nov 29, 2025
5362d31
refactor: better error reporting
robgee86 Dec 1, 2025
9029ab8
fix: fmt
robgee86 Dec 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/calculate-size-delta.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:

permissions:
contents: read
pull-requests: read

jobs:
build:
Expand Down
2 changes: 1 addition & 1 deletion containers/ei-models-runner/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# SPDX-License-Identifier: MPL-2.0

FROM public.ecr.aws/z9b3d4t5/inference-container-qc-adreno-702:4d7979284677b6bdb557abe8948fa1395dc89a63
FROM public.ecr.aws/z9b3d4t5/inference-container-qc-adreno-702:a7ec92c6c9b2b9c94255baed18328cf4e2fda6d0

# Create the user and group needed to run the container as non-root
RUN set -ex; \
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ dev = [
"setuptools",
"build",
"pytest",
"pytest-asyncio",
"websocket-client",
"ruff",
"docstring_parser>=0.16",
Expand Down
28 changes: 22 additions & 6 deletions src/arduino/app_bricks/camera_code_detection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ This Brick enables real-time barcode and QR code scanning from a camera video st

The Camera Code Detection Brick allows you to:

- Capture frames from a USB camera.
- Configure camera settings (resolution and frame rate).
- Capture frames from a Camera (see Camera peripheral for supported cameras).
- Configure Camera settings (resolution and frame rate).
- Define the type of code to detect: barcodes and/or QR codes.
- Process detections with customizable callbacks.

Expand All @@ -22,7 +22,7 @@ The Camera Code Detection Brick allows you to:

## Prerequisites

To use this Brick you should have a USB camera connected to your board.
To use this Brick you can choose to plug a camera to your board or use a network-connected camera.

**Tip**: Use a USB-C® Hub with USB-A connectors to support commercial web cameras.

Expand All @@ -37,9 +37,25 @@ def render_frame(frame):
def handle_detected_code(frame, detection):
...

# Select the camera you want to use, its resolution and the max fps
detection = CameraCodeDetection(camera=0, resolution=(640, 360), fps=10)
detection = CameraCodeDetection()
detection.on_frame(render_frame)
detection.on_detection(handle_detected_code)
detection.start()

App.run()
```

You can also select a specific camera to use:

```python
from arduino.app_bricks.camera_code_detection import CameraCodeDetection

def handle_detected_code(frame, detection):
...

# Select the camera you want to use, its resolution and the max fps
camera = Camera(camera="rtsp://...", resolution=(640, 360), fps=10)
detection = CameraCodeDetection(camera)
detection.on_detection(handle_detected_code)

App.run()
```
5 changes: 2 additions & 3 deletions src/arduino/app_bricks/camera_code_detection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#
# SPDX-License-Identifier: MPL-2.0

from .detection import Detection, CameraCodeDetection
from .utils import draw_bounding_boxes, draw_bounding_box
from .detection import CameraCodeDetection, Detection

__all__ = ["CameraCodeDetection", "Detection", "draw_bounding_boxes", "draw_bounding_box"]
__all__ = ["CameraCodeDetection", "Detection"]
28 changes: 14 additions & 14 deletions src/arduino/app_bricks/camera_code_detection/detection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
import threading
from typing import Callable

import cv2
from pyzbar.pyzbar import decode, ZBarSymbol, PyZbarError
import numpy as np
from PIL.Image import Image
from PIL.Image import Image, fromarray

from arduino.app_peripherals.usb_camera import USBCamera
from arduino.app_peripherals.camera import Camera, BaseCamera
from arduino.app_utils.image import greyscale
from arduino.app_utils import brick, Logger

logger = Logger("CameraCodeDetection")
Expand Down Expand Up @@ -44,7 +44,7 @@ class CameraCodeDetection:
"""Scans a camera video feed for QR codes and/or barcodes.

Args:
camera (USBCamera): The USB camera instance. If None, a default camera will be initialized.
camera (BaseCamera): The camera instance to use for capturing video. If None, a default camera will be initialized.
detect_qr (bool): Whether to detect QR codes. Defaults to True.
detect_barcode (bool): Whether to detect barcodes. Defaults to True.

Expand All @@ -55,18 +55,20 @@ class CameraCodeDetection:

def __init__(
self,
camera: USBCamera = None,
camera: BaseCamera = None,
detect_qr: bool = True,
detect_barcode: bool = True,
):
"""Initialize the CameraCodeDetection brick."""
if detect_qr is False and detect_barcode is False:
raise ValueError("At least one of 'detect_qr' or 'detect_barcode' must be True.")

self._camera = camera if camera else Camera()

self._detect_qr = detect_qr
self._detect_barcode = detect_barcode

# These callbacks do not require locks as long as we're running on CPython
# These callbacks don't require locking as long as we're running on CPython
self._on_frame_cb = None
self._on_error_cb = None

Expand All @@ -76,8 +78,6 @@ def __init__(

self.already_seen_codes = set()

self._camera = camera if camera else USBCamera()

def start(self):
"""Start the detector and begin scanning for codes."""
self._camera.start()
Expand Down Expand Up @@ -154,13 +154,13 @@ def loop(self):
self._on_error(e)
return

# Use grayscale for barcode/QR code detection
gs_frame = cv2.cvtColor(np.asarray(frame), cv2.COLOR_RGB2GRAY)

self._on_frame(frame)
pil_frame = fromarray(frame)
self._on_frame(pil_frame)

# Use grayscale for barcode/QR code detection
gs_frame = greyscale(frame)
detections = self._scan_frame(gs_frame)
self._on_detect(frame, detections)
self._on_detect(pil_frame, detections)

def _on_frame(self, frame: Image):
if self._on_frame_cb:
Expand All @@ -170,7 +170,7 @@ def _on_frame(self, frame: Image):
logger.error(f"Failed to run on_frame callback: {e}")
self._on_error(e)

def _scan_frame(self, frame: cv2.typing.MatLike) -> list[Detection]:
def _scan_frame(self, frame: np.ndarray) -> list[Detection]:
"""Scan the frame for a single barcode or QR code."""
detections = []

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ def on_codes_detected(frame: Image, detections: list[Detection]):
detector = CameraCodeDetection()
detector.on_detect(on_codes_detected)

App.run() # This will block until the app is stopped
App.run()
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# EXAMPLE_REQUIRES = "Requires an USB webcam connected to the Arduino board."
from PIL.Image import Image
from arduino.app_utils.app import App
from arduino.app_peripherals.usb_camera import USBCamera
from arduino.app_peripherals.usb_camera import Camera
from arduino.app_bricks.camera_code_detection import CameraCodeDetection, Detection


Expand All @@ -17,7 +17,7 @@ def on_code_detected(frame: Image, detection: Detection):
# e.g., draw a bounding box, save it to a database or log it.


camera = USBCamera(camera=0, resolution=(640, 360), fps=10)
camera = Camera(camera=2, resolution=(640, 360), fps=10)
detector = CameraCodeDetection(camera)
detector.on_detect(on_code_detected)

Expand Down
19 changes: 10 additions & 9 deletions src/arduino/app_bricks/object_detection/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,24 @@ The Object Detection Brick allows you to:
```python
import os
from arduino.app_bricks.object_detection import ObjectDetection
from arduino.app_utils.image import draw_bounding_boxes

object_detection = ObjectDetection()

# Image frame can be as bytes or PIL image
frame = os.read("path/to/your/image.jpg")
# Image can be provided as bytes or PIL.Image
img = os.read("path/to/your/image.jpg")

out = object_detection.detect(frame)
# is it possible to customize image type, confidence level and box overlap
# out = object_detection.detect(frame, image_type = "png", confidence = 0.35, overlap = 0.5)
out = object_detection.detect(img)
# You can also provide a confidence level
# out = object_detection.detect(frame, confidence = 0.35)
if out and "detection" in out:
for i, obj_det in enumerate(out["detection"]):
# For every object detected, get its details
# For every object detected, print its details
detected_object = obj_det.get("class_name", None)
bounding_box = obj_det.get("bounding_box_xyxy", None)
confidence = obj_det.get("confidence", None)
bounding_box = obj_det.get("bounding_box_xyxy", None)

# draw the bounding box and key points on the image
out_image = object_detection.draw_bounding_boxes(frame, out)
# Draw the bounding boxes
out_image = draw_bounding_boxes(img, out)
```

3 changes: 2 additions & 1 deletion src/arduino/app_bricks/object_detection/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from typing import Any

from PIL import Image
from arduino.app_utils import brick, Logger, draw_bounding_boxes, Shape
from arduino.app_utils import brick, Logger
from arduino.app_utils.image import draw_bounding_boxes, Shape
from arduino.app_internal.core import EdgeImpulseRunnerFacade

logger = Logger("ObjectDetection")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,24 @@
# SPDX-License-Identifier: MPL-2.0

# EXAMPLE_NAME = "Object Detection"
import os
from arduino.app_bricks.object_detection import ObjectDetection
from arduino.app_utils.image import draw_bounding_boxes

object_detection = ObjectDetection()

# Image frame can be as bytes or PIL image
with open("image.png", "rb") as f:
frame = f.read()
# Image can be provided as bytes or PIL.Image
img = os.read("path/to/your/image.jpg")

out = object_detection.detect(frame)
# is it possible to customize image type, confidence level and box overlap
# out = object_detection.detect(frame, image_type = "png", confidence = 0.35, overlap = 0.5)
out = object_detection.detect(img)
# You can also provide a confidence level
# out = object_detection.detect(frame, confidence = 0.35)
if out and "detection" in out:
for i, obj_det in enumerate(out["detection"]):
# For every object detected, get its details
# For every object detected, print its details
detected_object = obj_det.get("class_name", None)
bounding_box = obj_det.get("bounding_box_xyxy", None)
confidence = obj_det.get("confidence", None)
bounding_box = obj_det.get("bounding_box_xyxy", None)

# draw the bounding box and key points on the image
out_image = object_detection.draw_bounding_boxes(frame, out)
# Draw the bounding boxes
out_image = draw_bounding_boxes(img, out)
Loading