Color recognition with Raspberry Pi

We know how to build. We know how to run. We even know how to use different architectures. Time to start doing something useful.

Our goal: deploy a simple, containerized application that will display a color seen by the Raspberry Pi’s camera*. Described application and a docker config file can be found here.

Because we target the ARMv6 architecture, I decided to base the docker image on Alpine Linux. It supports many different architectures and is very small in size (5MB for minimal version). Let’s add OpenCV lib and the target application on top of it. Below is Docker config file.

FROM arm32v6/alpine

ARG OPENCV_VERSION=3.1.0

RUN apk add --no-cache \
    linux-headers \
    gcc \
    g++ \
    git \
    make \
    cmake \
    raspberrypi

RUN wget https://github.com/opencv/opencv/archive/${OPENCV_VERSION}.zip && \
    unzip ${OPENCV_VERSION}.zip && \
    rm -rf ${OPENCV_VERSION}.zip && \
    mkdir -p opencv-${OPENCV_VERSION}/build && \
    cd opencv-${OPENCV_VERSION}/build && \
    cmake \
    -D CMAKE_BUILD_TYPE=RELEASE \
    -D CMAKE_INSTALL_PREFIX=/usr/local \
    -D WITH_FFMPEG=NO \
    -D WITH_IPP=NO \
    -D WITH_OPENEXR=NO \
    -D WITH_TBB=YES \
    -D BUILD_EXAMPLES=NO \
    -D BUILD_ANDROID_EXAMPLES=NO \
    -D INSTALL_PYTHON_EXAMPLES=NO \
    -D BUILD_DOCS=NO \
    -D BUILD_opencv_python2=NO \
    -D BUILD_opencv_python3=NO \
    .. && \
    make -j nproc && \
    make install && \
    rm -rf /opencv-${OPENCV_VERSION} 

COPY src/** /app/
RUN mkdir -p /app/build && cd /app/build && cmake .. && \ 
    make && \
    make install && \
    rm /app -rf 

ENTRYPOINT ["/usr/local/bin/opencv_hist"]
CMD ["0"] 

I set my binary as an entry point so it will run when the container is started. I also use CMD to set a default parameter which is a camera index. If not given, 0 will be used. Now we can build and push this image to the Docker hub (or another Docker registry).

$ docker build --rm -t 4point2software/rpi0 .
$ docker push 4point2software/rpi0

Assuming you have a camera attached and configured on our Pi0, you can execute the following lines on the target machine.

$ docker pull 4point2software/rpi0
$ docker run --device /dev/video0 4point2software/rpi0

This should result in an output that will change every time you present a different color to your camera.

A very nice thing about building applications inside containers is that they are completely independent of the target file system. The only requirement is the docker engine – all the rest we ship with our app. Different architecture needed? Just change the base image (eg. arm32v7/alpine). No need for setting up a new tool-chain, cross-compiling, and all related stuff. Think about sharing build environments, CI builds, and you will definitely consider this as something worth trying.

* I will be using a Raspberry Pi 0 with a camera module to get the peak value of the HS histogram

Building Docker image for Raspberry Pi

We like containers and we like Pi computers. Containers running on our Pi’s would be like “double like” and although “double like” does not exist in the real word* running Docker on Pi is still possible.

Good (though little bit outdated) instruction on how to install docker on your board can be found in the first part of this article. Short copy-paste is here:

$ sudo apt-get install apt-transport-https ca-certificates software-properties-common -y
$ curl -fsSL get.docker.com -o get-docker.sh && sh get-docker.sh
$ sudo usermod -aG docker pi
$ sudo reboot
$ sudo systemctl start docker.service

After following described steps you should be able to run docker engine and start / stop containers.

Lets now try to create a sample image. Please note that we do this on our host machine as our experience tells us that its always faster and does not require putting cups with cold water on top of Pi CPU. We search for “raspbian” on Docker hub and we find raspbian/stretch image which should be good to start with. We will add a boost library just to have some extra example layer.

FROM raspbian/stretch
RUN apt-get update && apt-get install -y \
   libboost1.62-all \
   && rm -rf /var/lib/apt/lists/*

We try to build it with:

$ docker build -t rpi_test .

And we get following error:

Sending build context to Docker daemon  2.048kB
 ...
 standard_init_linux.go:211: exec user process caused "exec format error"
 ...

So it is almost working. Just not quite yet. The problem is that our Pi is an arm image (which makes sense as Pi is an arm device). You can check it by running:

$ sudo docker image inspect raspbian/stretch | grep Arch
   "Architecture": "arm",

How to run an arm image on x86 architecture? Use emulator. Quemu was always helping us in such cases so lets make it do its magic for us.

$ sudo apt install qemu-user-static
$ docker run --rm --privileged multiarch/qemu-user-static --reset -p yes

First line installs emulator. The second tells our kernel to open arm (and other architecture) binaries with quemu. You can do this part manually by running. In this case we register only armv7 arch so you would need to repeat this step for every architecture you would like to use (with correct magic strings).

$ sudo sh -c "echo -1 > /proc/sys/fs/binfmt_misc/qemu-arm"
$ sudo sh -c "echo ':qemu-arm:M::\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x28\x00:\xff\xff\xff\xff\xff\xff\xff\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff:/usr/bin/qemu-arm-static:OCF' > /proc/sys/fs/binfmt_misc/register"

Now we can not only build our custom images for Pi but also run them to inspect / adjust before deployment. You can push this image into your docker hub repo and try to pull it from the Raspberry Pi.

$ docker tag rpi_test <docker_id>/rpi_test
$ docker login
$ docker push <docker_id>/rpi_test

And on your Pi.

$ dokcer run -it <docker_id>/rpi_test /bin/bash

Should work :). 👍👍

* liking something twice results in not liking at all