Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
83 commits
Select commit Hold shift + click to select a range
40c86f6
feat: add new Camera factory
robgee86 Oct 16, 2025
04bde77
refactor: IPCamera
robgee86 Oct 20, 2025
f3399e8
refactor: streamer example
robgee86 Oct 20, 2025
5cf87a3
perf
robgee86 Oct 20, 2025
a97ac65
refactor: BaseCamera
robgee86 Oct 21, 2025
5cabd67
refactor
robgee86 Oct 21, 2025
8ca5218
refactor: image manipulation
robgee86 Oct 24, 2025
bd937d0
fix: pipelining
robgee86 Oct 27, 2025
7c8351b
refactor: remove adjust/adjusted functions
robgee86 Oct 27, 2025
e8162b1
feat: better support BGR, BGRA and uint8, uint16, uint32
robgee86 Oct 28, 2025
9ad9d6c
refactor: deprecate USBCamera in favor of Camera and make it compatible
robgee86 Oct 29, 2025
6d43e9f
doc: update docstrings
robgee86 Oct 29, 2025
9e5a0d3
tidy-up
robgee86 Oct 29, 2025
05a2818
refactor: clearer APIs and doc
robgee86 Oct 29, 2025
666f8ce
add examples
robgee86 Oct 29, 2025
7121aed
refactor: directly use V4LCamera
robgee86 Oct 29, 2025
6f79d43
remove test examples
robgee86 Oct 29, 2025
1cc6d52
fix: race condition
robgee86 Oct 29, 2025
4039a38
doc: update readme
robgee86 Oct 29, 2025
81d4310
refactor
robgee86 Oct 29, 2025
e2574cb
doc: explain adjustments argument
robgee86 Oct 29, 2025
7671016
run fmt
robgee86 Oct 30, 2025
a439e27
run linter
robgee86 Oct 30, 2025
76e5b39
fix: wrong import
robgee86 Oct 30, 2025
b5499af
perf
robgee86 Oct 30, 2025
c451e06
fix: numeric issue
robgee86 Oct 30, 2025
a28ce9e
refactor: change default image serialization format
robgee86 Oct 30, 2025
f5f5852
perf: reduce buffer size to lower latency
robgee86 Oct 30, 2025
9e68976
doc: better clarify supported image formats
robgee86 Oct 31, 2025
b96c935
feat: allow also image formats with higher bit depth and preserve all…
robgee86 Oct 31, 2025
a98b301
chore: run fmt
robgee86 Oct 31, 2025
6e9ba97
refactor: Camera args
robgee86 Oct 31, 2025
2b3c335
perf: make resize a no-op if frame has already target size
robgee86 Nov 3, 2025
f1fe8ef
refactor
robgee86 Nov 4, 2025
122fc51
feat: update EI container to add TCP streaming mode
robgee86 Nov 4, 2025
c230a7b
refactor: migrate camera_code_detection
robgee86 Nov 4, 2025
7c4a435
refactor: migrate vide_objectdetection
robgee86 Nov 4, 2025
b78d38c
refactor: migrate video_objectdetection
robgee86 Nov 4, 2025
42d626c
refactor
robgee86 Nov 6, 2025
2632f5b
fix
robgee86 Nov 10, 2025
764c371
refactors
robgee86 Nov 13, 2025
2db1c04
add tests dependency
robgee86 Nov 13, 2025
b09b380
test: add tests for camera module
robgee86 Nov 13, 2025
342ea63
fixes
robgee86 Nov 13, 2025
40426d5
fix: API
robgee86 Nov 13, 2025
1037c42
fix: linting
robgee86 Nov 13, 2025
dc73d54
chore: bump EI base image version
robgee86 Nov 13, 2025
b7bc786
fix: missing job permissions
robgee86 Nov 13, 2025
23808a3
fix: port must be an int
robgee86 Nov 13, 2025
fff9909
feat: add automatic reconnection
robgee86 Nov 14, 2025
b2cb327
feat: resolve device to stable links for more reliable reconnections
robgee86 Nov 15, 2025
8664292
refactor
robgee86 Nov 15, 2025
df6060b
fix: test race condition
robgee86 Nov 15, 2025
99bd9cd
return exception when capturing if camera has not been started
robgee86 Nov 15, 2025
cf2f340
refactor
robgee86 Nov 15, 2025
6c31fba
refactor
robgee86 Nov 15, 2025
6fe6941
fix tests
robgee86 Nov 15, 2025
733633c
fix: split frame capture and forwarding
robgee86 Nov 16, 2025
0d7190b
Revert "fix: split frame capture and forwarding"
robgee86 Nov 16, 2025
9842883
fix: race condition
robgee86 Nov 16, 2025
31ef731
refactor
robgee86 Nov 16, 2025
cb97837
feat: allow to subscribe to camera lifecycle events
robgee86 Nov 18, 2025
e38938d
feat: add support for 'paused' and 'resumed' events
robgee86 Nov 18, 2025
6956ad3
refactor
robgee86 Nov 18, 2025
bf50c24
fix: linting
robgee86 Nov 18, 2025
8edbc6d
feat: implement a (simple) state machine for state management
robgee86 Nov 18, 2025
c42a5f6
fix
robgee86 Nov 18, 2025
96cf718
refactor: remove host from WebSocketCamera
robgee86 Nov 18, 2025
a327dba
refactor: rename address -> url
robgee86 Nov 18, 2025
69b7b4c
validate frame_format
robgee86 Nov 18, 2025
90ca66a
fix signature
robgee86 Nov 19, 2025
10e8b5b
feat: add recording to camera
robgee86 Nov 20, 2025
4bfcc44
fix: fmt
robgee86 Nov 20, 2025
87fc6ff
feat: add support to authentication and encryption
robgee86 Nov 22, 2025
f724a06
chore: update EI container version
robgee86 Nov 22, 2025
06eb4b6
feat: add utilities for flipping images
robgee86 Nov 23, 2025
47886f1
feat: change protocol implementation
robgee86 Nov 26, 2025
66ba4a5
fix: prime the EI pipeline at startup
robgee86 Nov 26, 2025
a39014c
refactor: better error logging
robgee86 Nov 26, 2025
92ce608
fix: linting
robgee86 Nov 26, 2025
5f1172e
chore: update license header
robgee86 Nov 26, 2025
599e437
fix: imports
robgee86 Nov 26, 2025
63a33f9
refactor: align websocket camera with other peripheral implementations
robgee86 Nov 27, 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",
"ruff",
"docstring_parser>=0.16",
"arduino_app_bricks[all]",
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