pythondockerraspberry-pipicamera

Error accessing picamera in Docker container running on Raspberry Pi


I'm running a Docker container using docker-compose on Raspberry Pi 4 and would like to use the picamera module in Python to access a PiHQCam. I tried to get it to work using this tutorial: https://www.losant.com/blog/how-to-access-the-raspberry-pi-camera-in-docker. However, when trying to build the image, it gave me this error:

ValueError: This system does not appear to be a Raspberry Pi

I then tried my own solution (see below), but met this error:

picamera.exc.PiCameraMMALError: Failed to create MMAL component b'vc.camera_info': I/O error

Here's my setup:

docker-compose.yml:

version: '3.8'

services: 
  camera:
    build: camera
    image: eye_camera     
    devices:
      - /dev/vchiq

Dockerfile:

FROM balenalib/rpi-raspbian:latest

RUN apt-get update && \
    apt-get upgrade && \
    apt-get install -y python3 \
      python3-pip

WORKDIR /app

COPY requirements.txt .
RUN pip3 install -r requirements.txt

COPY . .

RUN groupadd -r -g 888 app && \
    useradd -r -u 888 -g app -d /app app && \
    chown -R app:app /app && \
    usermod -a -G video app
USER app

CMD ["python3", "./main.py"]

main.py:

import picamera

def main():
    print("Hello World!")

    cam = picamera.PiCamera()
    img = picamera.array.PiRGBArray(cam)
    cam.capture(img)
    cam.close()

if __name__ == '__main__':
    main()

The PiCameraMMALError occurs right when initialising the camera. This is the full error output:

camera_1 | Hello World!
camera_1 | Traceback (most recent call last):
camera_1 |   File "main.py", line 19, in <module>
camera_1 |     main()
camera_1 |   File "main.py", line 12, in main
camera_1 |     cam = picamera.PiCamera()
camera_1 |   File "/usr/local/lib/python3.7/dist-packages/picamera/camera.py", line 367, in __init__
camera_1 |     with mo.MMALCameraInfo() as camera_info:
camera_1 |   File "/usr/local/lib/python3.7/dist-packages/picamera/mmalobj.py", line 2346, in __init__
camera_1 |     super(MMALCameraInfo, self).__init__()
camera_1 |   File "/usr/local/lib/python3.7/dist-packages/picamera/mmalobj.py", line 633, in __init__
camera_1 |     prefix="Failed to create MMAL component %s" % self.component_type)
camera_1 |   File "/usr/local/lib/python3.7/dist-packages/picamera/exc.py", line 184, in mmal_check
camera_1 |     raise PiCameraMMALError(status, prefix)
camera_1 | picamera.exc.PiCameraMMALError: Failed to create MMAL component b'vc.camera_info': I/O error
camera_1 | mmal: mmal_vc_shm_init: could not initialize vc shared memory service
camera_1 | mmal: mmal_vc_component_create: failed to initialise shm for 'vc.camera_info' (7:EIO)
camera_1 | mmal: mmal_component_create_core: could not create component 'vc.camera_info' (7)

What's the problem here? Thanks for any help! I'll be happy to provide more info :)


Solution

  • For some utterly unfathomable reason you need the magic incantation "READTHEDOCS=True" to avoid this problem! The following Dockerfile works fine on a Raspberry Pi 4 with it, but without it the Docker image build fails with the error you are seeing :

    FROM arm32v7/python:latest
    
    # Set in container
    ENV TZ=Europe/London
    
    # See https://github.com/waveform80/picamera/issues/578
    ENV READTHEDOCS=True
    
    COPY ./src /app    
    
    WORKDIR /app
    
    RUN pip install --upgrade pip setuptools wheel
    
    RUN pip install picamera
    
    VOLUME ["/data"]
    
    CMD ["/usr/bin/python", "camera.py"]
    

    I built this with the docker build command :

    docker build -t jrcamera:latest .
    

    and ran it with the docker run command :

    docker run --privileged=true -v /opt/vc:/opt/vc --env LD_LIBRARY_PATH=/opt/vc/lib --device /dev/vchiq -it jrcamera:latest
    

    and it ran a treat with the following python script, camera.py, in the src directory (copied to /app in the Docker image) :

    from time import sleep
    from datetime import datetime
    from picamera import PiCamera
    
    camera = PiCamera()
    
    camera.resolution = (1280, 720)
    
    camera.annotate_text_size = 16  # Can be in range 6 to 160 inclusive, with default 32
    
    MAX_ITER = 5
    
    for iter_no in range(0,MAX_ITER):
    
        camera.start_preview()
    
        #  It is important to sleep for at least two seconds before capturing an image,
        #  because this gives the cameras sensor time to sense the light levels
        #
        sleep(3)
    
        # Annotate image with current datetime, to nearest millisecond
        #
        date_time = datetime.utcnow().strftime('%Y-%m-%d_%H-%M-%S.%f')[:-3]
    
        camera.annotate_text = date_time
    
        camera.capture("/data/image_{0:}.jpg".format(date_time))
    
        camera.stop_preview()
    

    (Now I just have to figure out how to reduce my image size for this from a ridiculously large 710 MBytes, but that is another problem.)