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_types
in.actdk/setting.json
and includeraspberrypi-bullseye
. -
Include the
raspberrypi-bullseye
section added in 1 in the.actdk/dependencies.json
file. -
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_device
from theactfw_core.system
module for CSI cameras, andfind_usb_camera_device
from the same module for USB camera devices. -
To use a CSI camera with a Raspberry Pi Bullseye, use
UnicamIspCapture
from theactfw_core.unicam_isp_capture
module. To use a USB camera with a Raspberry Pi Buster or Bullseye, useV4LCameraCapture
from theactfw_core.capture
module. -
The layer passed to the argument of the
open_window
function of theactfw_raspberrypi.vc4
module 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_window
method of theactfw_raspberrypi.vc4.Display
class is unique within the application. -
Do not use the
actfw_raspberrypi.vc4.Display
class when a display is not connected.