Bullseye Migration Guide
In Actcast OS 3, the base OS is Raspberry Pi Bullseye, so modifications are required to the application configuration file and code.
Here, we will explain how to make an existing application running on Raspberry Pi Buster compatible with Raspberry Pi Bullseye, using ImageNet Classification as an example.
See here for the entire code.
Advance preparation and prior knowledge
As a prerequisite, we will assume that you have an application being maintained on Actcast.
Please install the latest ActDK.
You can use the API Token as it is. There is also no need to obtain a new application ID.
Overview of the corrections
First, we will list the items that need to be corrected.
-
Indicate
target_typesin.actdk/setting.jsonand includeraspberrypi-bullseye. -
Include the
raspberrypi-bullseyesection added in 1 in the.actdk/dependencies.jsonfile. -
Add a manifest file for Bullseye to
manifesto/. -
Make corrections to use different APIs depending on whether you use the official Raspberry Pi CSI camera or a USB camera as the camera device.
The specific correction methods for each are explained below.
Modify .actdk/setting.json
Actcast supports raspberrypi-buster (or raspberrypi) as well as raspberrypi-bullseye as a firmware type.
From now on, the app needs to specify which firmware type it is targeting.
Specify a list of target_types as follows:
{
"app_id": "imagenet-classification-for-raspi",
"app_server_id": 1,
"short_description": "1000 class ImageNet classification demo for raspi",
"short_descriptions": {
"ja": "ImageNet1000γ―γ©γΉει‘γγ’(for raspi)"
- }
+ },
+ "target_types": ["raspberrypi-buster", "raspberrypi-bullseye"]
}
Modifying .actdk/dependencies.json
Add a section for each firmware type added to .actdk/setting.json.
If the existing settings use the name raspberrypi, change it to raspberrypi-buster to match .actdk/setting.json.
raspberrypi may be deprecated in the future.
In addition to this, add a raspberrypi-bullseye section.
Add apt and pip dependencies as necessary.
{
"apt": [
"libv4l-0",
"libv4lconvert0",
"python3-pil",
"python3-numpy",
"fonts-dejavu-core"
],
"pip": [],
- "raspberrypi": {
+ "raspberrypi-buster": {
"apt": [
"libraspberrypi0"
],
"pip": [
"actfw-raspberrypi"
]
- }
+ },
+ "raspberrypi-bullseye": {
+ "apt": [
+ "libraspberrypi0"
+ ],
+ "pip": [
+ "actfw-raspberrypi"
+ ]
+ }
}
Modifying and adding manifesto files
Modify the existing manifest file.
First, set version to 2.
Specify raspberrypi-buster as target_type.
{
+ "version": 2,
+ "target_type": "raspberrypi-buster",
"boards": [
"RSPi3B",
"RSPi3BPlus",
"RSPi3APlus",
"RSPi4B"
],
"devices": [
{ "type": "camera" },
{ "type": "videocore" },
{
"type": "framebuffer",
"device": [
"/dev/fb0"
],
"required": false
}
]
}
Next, add a new manifesto/bullseye.json file.
The file name does not matter as long as it is under the manifesto directory.
However, be careful not to duplicate target_type.
The device files used for some device types differ between raspberrypi-buster and raspberrypi-bullseye.
If you omit the device file specification, as in camera and videocore below, the device file normally used for each device type will be automatically mounted. However, please note that there are some device types for which you cannot omit the device file specification.
For details, see Application Schemas.
+ {
+ "version": 2,
+ "target_type": "raspberrypi-bullseye",
+ "boards": [
+ "RSPi3B",
+ "RSPi3BPlus",
+ "RSPi3APlus",
+ "RSPi4B"
+ ],
+ "devices": [
+ { "type": "camera" },
+ { "type": "videocore" },
+ {
+ "type": "framebuffer",
+ "device": [
+ "/dev/fb0"
+ ],
+ "required": false
+ }
+ ]
+ }
Modifying the program
Now we will modify the program. There are three changes to make:
-
To detect the camera device, use
find_csi_camera_devicefrom theactfw_core.systemmodule for CSI cameras, andfind_usb_camera_devicefrom the same module for USB camera devices. -
To use a CSI camera with a Raspberry Pi Bullseye, use
UnicamIspCapturefrom theactfw_core.unicam_isp_capturemodule. To use a USB camera with a Raspberry Pi Buster or Bullseye, useV4LCameraCapturefrom theactfw_core.capturemodule. -
The layer passed to the argument of the
open_windowfunction of theactfw_raspberrypi.vc4module must be a positive number less than or equal to 16.
For ImageNet Classification, we added a setting value called use_usb_camera to tell the app whether the camera is a USB camera or not in order to determine condition 2.
Below are the changes made to setting_schema.json and act_settings.json.
{
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"display": {
"title": "display",
"description": "output video to HDMI display",
"type": "boolean",
"default": false
},
"threshold": {
"title": "probability threshold",
"description": "notify when over this threshold",
"type": "number",
"default": 0.9,
"minimum": 0,
"maximum": 1
- }
+ },
+ "use_usb_camera": {
+ "title": "use USB camera",
+ "description": "use USB camera instead of Raspberry Pi Camera",
+ "type": "boolean",
+ "default": false
+ }
},
"required": [
"display",
"threshold"
],
"propertyOrder": [
"display",
- "threshold"
+ "threshold",
+ "use_usb_camera"
]
}
{
"display": true,
- "threshold": 0.1
+ "threshold": 0.1,
+ "use_usb_camera": false
}
Finally, here is the difference when the above changes are made to app/main.
#!/usr/bin/env python3
import argparse
from PIL import Image, ImageDraw, ImageFont
import actfw_core
from actfw_core.task import Pipe, Consumer
+ from actfw_core.system import get_actcast_firmware_type, find_csi_camera_device, find_usb_camera_device
from actfw_core.capture import V4LCameraCapture
+ from actfw_core.unicam_isp_capture import UnicamIspCapture
import actfw_raspberrypi
from actfw_raspberrypi.vc4 import Display
import numpy as np
from model import Model
(CAPTURE_WIDTH, CAPTURE_HEIGHT) = (224, 224) # capture image size
(DISPLAY_WIDTH, DISPLAY_HEIGHT) = (640, 480) # display area size
class Classifier(Pipe):
def __init__(self, capture_size):
super(Classifier, self).__init__()
self.model = Model()
self.capture_size = capture_size
def proc(self, frame):
rgb_image = Image.frombuffer('RGB', self.capture_size, frame.getvalue(), 'raw', 'RGB')
rgb_image = rgb_image.resize((CAPTURE_WIDTH, CAPTURE_HEIGHT))
input_image = np.asarray(rgb_image).reshape(1, CAPTURE_WIDTH, CAPTURE_HEIGHT, 3).astype(np.float32)
probs, = self.model.MobileNet_v2(input_image)
return (rgb_image, probs[0][1:])
class Presenter(Consumer):
def __init__(self, settings, preview_window, cmd):
super(Presenter, self).__init__()
self.settings = settings
self.preview_window = preview_window
self.cmd = cmd
self.font = ImageFont.truetype(font='/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf', size=18)
with open('labels.txt') as f:
self.labels = f.read().splitlines()
def proc(self, images):
rgb_image, probs = images
top1 = probs.argsort()[-1]
if probs[top1] > self.settings['threshold']:
actfw_core.notify([{'prob': float(probs[top1]), 'label': self.labels[top1]}])
self.cmd.update_image(rgb_image) # update `Take Photo` image
actfw_core.heartbeat()
if self.preview_window is not None:
draw = ImageDraw.Draw(rgb_image)
draw.text((0, 0), "{:>6.2f}% {}".format(100 * probs[top1], self.labels[top1]), font=self.font, fill=(0, 255, 0))
self.preview_window.blit(rgb_image.tobytes())
self.preview_window.update()
+ def setup_camera_capture(use_usb_camera):
+ framerate = 15
+ device = find_usb_camera_device() if use_usb_camera else find_csi_camera_device()
+
+ if get_actcast_firmware_type() == "raspberrypi-bullseye" and not use_usb_camera:
+ # Use UnicamIspCapture on Raspberry Pi OS Bullseye
+ cap = UnicamIspCapture(unicam=device, size=(CAPTURE_WIDTH, CAPTURE_HEIGHT), framerate=framerate)
+ else:
+ # Use V4LCameraCapture on Raspberry Pi OS Buster or use USB camera
+ cap = V4LCameraCapture(device, (CAPTURE_WIDTH, CAPTURE_HEIGHT), framerate, format_selector=V4LCameraCapture.FormatSelector.PROPER)
+
+ return cap
def main(args):
# Actcast application
app = actfw_core.Application()
# Load act setting
- settings = app.get_settings({'display': True, 'threshold': 0.1})
+ settings = app.get_settings({'display': True, 'threshold': 0.1, 'use_usb_camera': False})
# CommandServer (for `Take Photo` command)
cmd = actfw_core.CommandServer()
app.register_task(cmd)
- # Capture task
- cap = V4LCameraCapture('/dev/video0', (CAPTURE_WIDTH, CAPTURE_HEIGHT), 15, format_selector=V4LCameraCapture.FormatSelector.PROPER)
+ cap = setup_camera_capture(settings['use_usb_camera'])
capture_size = cap.capture_size()
app.register_task(cap)
# Classifier task
conv = Classifier(capture_size)
app.register_task(conv)
def run(preview_window=None):
# Presentation task
pres = Presenter(settings, preview_window, cmd)
app.register_task(pres)
# Make task connection
cap.connect(conv) # from `cap` to `conv`
conv.connect(pres) # from `conv` to `pres`
# Start application
app.run()
if settings['display']:
with Display() as display:
display_width, display_height = display.size()
scale = min(float(display_width / CAPTURE_WIDTH), float(display_height / CAPTURE_WIDTH))
width = int(scale * CAPTURE_WIDTH)
height = int(scale * CAPTURE_HEIGHT)
left = (display_width - width) // 2
upper = (display_height - height) // 2
- with display.open_window((left, upper, width, height), (CAPTURE_WIDTH, CAPTURE_HEIGHT), 1000) as preview_window:
+ # layer must be between 1 and 16
+ layer = 16
+ with display.open_window((left, upper, width, height), (CAPTURE_WIDTH, CAPTURE_HEIGHT), layer) as preview_window:
run(preview_window)
else:
run()
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='example: 1000 class classification')
main(parser.parse_args())
About Debugging
In the latest ActDK, there are two types of Actsim images: one for Buster and one for Bullseye. If you want to use both, please use the respective Actsim images for debugging and checking the operation.
Uploading your application
Uploading to Actcast remains unchanged. Uploading apps for both Buster and Bullseye will generate build images for both.
Other points to note
When making your Actcast application compatible with Bullseye, please also note the following points.
-
Make sure that the layer passed to the argument of the
open_windowmethod of theactfw_raspberrypi.vc4.Displayclass is unique within the application. -
Do not use the
actfw_raspberrypi.vc4.Displayclass when a display is not connected.