Welcome to part eight of our AI Powered Christmas Tree Series! The goal of this series is to serve as a guide for you to build your own AI powered Machine Learning Christmas Tree, thanks to the Arduino Portenta X8 SoM and Edge Impulse AI Platform.
In this tutorial, we will be guiding you though the process of interfacing your webcam with a Docker container. We will cover two methods of connecting a camera to a running container, and how to save these configurations in a Docker-compose app.
Before jumping into this entry, be sure to check out the previous blog entries. You can find a summary of each tutorial and a sneak peek of what’s coming next on our project overview page.
It can be tricky to interface an external device with a Docker container running on an embedded Linux device. In order for your Christmas Tree to detect a human presence, you need to connect a webcam—so that it can “see”—to our Arduino Portenta X8, which will be running our ML algorithm in a container. Typically, this communication between an external device and a host operating system can be difficult, but we are going to guide you through every step, including deploying the finished application on your Factory.
Prerequisites
This is part eight of our AI Powered Christmas Tree Series. Please ensure you’ve completed the earlier parts before continuing.
You will also need:
- a USB Camera
Connecting the USB Camera to the SoM or Embedded Device
First, you are going to connect the webcam to the Arduino Portenta X8 (or other embedded device).
Once you have done that, log in, open a terminal, and run a dmesg
command to check the system messages log to see if the camera has been recognized:
device:~$ dmesg
[73651.352075] input: GENERAL WEBCAM: GENERAL WEBCAM as /devices/platform/scb/fd500000.pcie/pci0000:00/0000:00:00.0/0000:01:00.0/usb1/1-1/1-1.3/1-1.3:1.0/input/input3
After the camera has been successfully recognized, a /dev/video[X]
interface should appear on your device. Run the following command to identify it:
device:~$ ls -l /dev/video*
crw-rw---- 1 root video 81, 8 Oct 31 23:10 /dev/video0
crw-rw---- 1 root video 81, 9 Oct 31 23:10 /dev/video1
crw-rw---- 1 root video 81, 0 Apr 28 2022 /dev/video13
crw-rw---- 1 root video 81, 1 Apr 28 2022 /dev/video14
crw-rw---- 1 root video 81, 2 Apr 28 2022 /dev/video15
crw-rw---- 1 root video 81, 3 Apr 28 2022 /dev/video16
crw-rw---- 1 root video 81, 4 Apr 28 2022 /dev/video20
crw-rw---- 1 root video 81, 5 Apr 28 2022 /dev/video21
crw-rw---- 1 root video 81, 6 Apr 28 2022 /dev/video22
crw-rw---- 1 root video 81, 7 Apr 28 2022 /dev/video23
In this case, the camera shows up as /dev/video0
.
Now that we have identified the correct node in /dev
, we will be able to interact directly with the camera.
Building Your Docker Image
For testing your application, instead of using software on the host (LmP), you will install everything in a Docker Container and test it with Docker, live on the device.
Docker is included in the Linux microPlatform, but you will need to add some additional packages to the basic image in order to access the camera and take a test picture.
To do this, let’s create a Dockerfile
with the instructions to create our Docker image with everything you need.
On your embedded device, create a new Dockerfile
:
vim Dockerfile
FROM debian:bullseye-slim
RUN
RUN apt-get update -y && \
apt-get install -y --no-install-recommends \
fswebcam \
v4l-utils
Build this Dockerfile
in the Docker Image:
device:~$ docker build -t camera-test .
Running the Docker Image
The next challenge is to expose the camera connected to your embedded device to the running Docker container. There are a few ways to do this; the simplest of which is to allow the container full access to the /dev
directory. This is not ideal, as it may expose various interfaces to the container which you may not want.
For now, it is a quick and easy way for you to test the camera.
Run the following command to launch the container:
device:~$ docker run -it --privileged -v /var/rootdirs/home/fio/image/:/image/ camera-test
This launches the container with full root privileges (--privileged
) which, among many other things, gives it access to the /dev
directory. It also creates a shared volume (-v
) between the host directory /var/rootdirs/home/fio/image/
, and the container's /image/
directory, allowing data exchange and manipulation between the host and the container.
The container also launches into an interactive session (-it
flag), so you will immediately be able to issue commands and navigate directories inside the container.
Navigate to /image
and run the following command to take a picture:
device-docker:~$ cd /image/
device-docker:~$ fswebcam -r 1920x1080 --jpeg 95 -D 1 web-cam-shot.jpg
--- Opening /dev/video0...
Trying source module v4l2...
/dev/video0 opened.
No input was specified, using the first.
Delaying 1 seconds.
--- Capturing frame...
Captured frame in 0.00 seconds.
--- Processing captured image...
Setting output format to JPEG, quality 95
Writing JPEG image to 'web-cam-shot.jpg'.
Now open a second terminal on the embedded device, navigate to the shared volume location, and use scpto copy the picture to your host machine:
device:~$ cd /var/rootdirs/home/fio/image
device:~$ scp web-cam-shot.jpg <host-user>@<host-ip>:~
This will send the picture to your host machine! The above command will place it in the home directory, but you may specify any location to save the picture.
Connecting the Webcam the Right Way with cgroup
As mentioned, privileged is not the best approach, as it gives too much power to the container. This elevated privilege level essentially removes the majority of security barriers and isolations that Docker usually applies to containers.
The right way to share the camera is to specify the camera device cgroup, sharing just the camera device.
First, find the device cgroup
by running ls -l
to find the camera:
device:~$ ls -l /dev/video0
crw-rw---- 1 root video 81, 8 Oct 31 23:10 /dev/video0
The 81, 8
is the camera's major and minor device number. With this we can alter the cgroup
permissions pertaining to the camera.
The new command to run the container is as follows:
device:~$ docker run -it \
--device-cgroup-rule='c 81:* rmw' \
-v /dev/video0:/dev/video0 \
-v /var/rootdirs/home/fio/image/:/image/ \
camera-test
Rather than elevating the Docker container's permissions with the --privileged
flag, you instead grant only the necessary permissions to interact with the camera via the --device-cgroup-rule
flag.
c
: Denotes the device type. In this case, a character device.81:*
: Specifies the major device number as81
, and allows any minor device number (*). It defines which device or range of devices the rule applies to.rmw
:r
: Grants read access to the specified device(s) within the cgroup.m
: Allows device modification or management permissions.w
: Provides write access to the device(s).
-v /dev/video0:/dev/video0
: share just thevideo0
device — which the container now has relevant permissions to access.-v /var/rootdirs/home/fio/image/:/image/
: creates the shared volume to save the pictures.
Let’s try the new version of our run command:
device:~$ docker run -it --device-cgroup-rule='c 81:* rmw' -v /dev/video0:/dev/video0 -v /var/rootdirs/home/fio/image/:/image/ camera-test
device-docker:~$ cd /image/
device-docker:~$ fswebcam -r 1920x1080 --jpeg 95 -D 1 web-cam-shot.jpg
--- Opening /dev/video0...
Trying source module v4l2...
/dev/video0 opened.
No input was specified, using the first.
Delaying 1 seconds.
--- Capturing frame...
Captured frame in 0.00 seconds.
--- Processing captured image...
Setting output format to JPEG, quality 95
Writing JPEG image to 'web-cam-shot.jpg'.
On a second terminal, copy the picture again:
device:~$ cd /var/rootdirs/home/fio/image
device:~$ scp web-cam-shot.jpg <host-user>@<host-ip>:~
That will send the picture to your host machine.
Docker Compose Application
These configurations for running the build command can be included in docker-compose.yaml
. This ensures the container will always be built in the same manner and with adequate permissions.
Below is what docker-compose.yaml
needs in order to build the same container as our Docker run command.
Create the following docker-compose.yaml
file in the same directory as your Dockerfile
:
device:~$ vim docker-compose.yaml
version: '3.6'
services:
camera-test:
image: camera-test
volumes:
- /dev/video0:/dev/video0
- /var/rootdirs/home/fio/image/:/image/
device_cgroup_rules:
- 'c 81:* rmw'
With this, you can build the same container as before, but instead of running the old long build command:
device:~$ docker run -it \
--device-cgroup-rule='c 81:* rmw' \
-v /dev/video0:/dev/video0 \
-v /var/rootdirs/home/fio/image/:/image/ \
camera-test
You can now simply run:
device:~$ docker-compose up
Recap and Conclusions
In this tutorial, we have successfully interfaced a webcam with a Docker container running on our embedded device. We covered a quick and dirty version using the --privileged
flag, and the more secure cgroups method. We also discussed using docker-compose.yaml
to easily recreate our container.
This was a prototype of the kind of configuration we will use to connect a webcam to a container running our ML algorithm to detect a human presence for our Christmas Tree.
Continue Building Ai-Powered Christmas Tree
If you are interested in our entire 14-part series on how to build an AI powdered Christmas tree, browse through each section below. Each part brings you closer to mastering your build:
- Part 1: Your First FoundriesFactory AI-Powered Christmas Tree
- Part 2: Configure Network and Wi-Fi
- Part 3: A Yocto & Linux Tutorial for Building An AI-Powered Christmas Tree
- Part 4: Linux MicroPlatform (lmP) Config Tutorial for AI-Powered Christmas Tree
- Part 5: Modify & Extend Linux MicroPlatform for AI-Powered Christmas Tree
- Part 6: Using a MQTT Switch Module on Arduino Portenta X8
- Part 7: Creating a Shell Script Application and Adding to Linux microPlatform™ (MQTT)
- Part 8: Connecting the Web Camera Interface with Docker Container
- Part 9: Using Edge Impulse AI to Recognize Human Presence With Arduino Portenta X8
- Part 10: Enhancing the Image Detection Capabilities of our AI Powered Christmas Tree
- Part 11: Writing a Python App to Turn Our Christmas Tree On/Off Using AI
- Part 12: Creating a Docker Compose App to Automatically Power Your AI Christmas Tree On/Off
- Part 13: Running Docker Compose Application on a Raspberry Pi 4 to Power Our AI Christmas Tree
- Part 14: Managing Multiple SoM Platforms in the Same Factory