PyRaspBeta

Camera (picamera2)

The Camera Module 3 plugs into the CSI ribbon connector; in circuit.py you wire pi.CSI to the camera's CSI pin and that is the whole circuit. On the software side, Picamera2 is the current camera library for the Pi — the old picamera does not run on current Raspberry Pi OS. PyRasp provides an implementation of Picamera2's common API. The official manual is Raspberry Pi's Picamera2 manual (PDF); the source lives at github.com/raspberrypi/picamera2.

Taking a photo

circuit.py
from wiring import *

pi = Pi()
camera = Camera()
pi.CSI >> camera.csi
main.py
from picamera2 import Picamera2
from time import sleep

camera = Picamera2()
camera.configure(camera.create_still_configuration())

camera.start()
camera.capture_file("photo.jpg")
camera.stop()

sleep(1)

capture_file() writes a JPEG or PNG into the Pi's home folder (/home/pi), chosen from the file extension, and in Lab mode the Files panel shows what was captured. Without the camera wired, Picamera2() raises IndexError: list index out of range, the same as the real library with no camera connected.

Capturing to memory

capture_array() returns a NumPy array of the frame; capture_image() returns a PIL image:

import numpy as np
from picamera2 import Picamera2

camera = Picamera2()
camera.configure(camera.create_preview_configuration())
camera.start()

frame = camera.capture_array()               # height × width × channels
print(frame.shape, frame.dtype)
print(frame.mean())                          # average brightness, 0 … 255

from PIL import Image
image = camera.capture_image()
image.save("from_pil.png")

camera.stop()                                # or camera.close()

capture_array() needs NumPy and capture_image() needs Pillow; both are available in the app.

Configurations and metadata

camera = Picamera2()

still = camera.create_still_configuration(main={"size": (1920, 1080)})
camera.configure(still)
camera.start()
metadata = camera.capture_file("shot.jpg")

print(metadata["SensorTimestamp"], metadata["ExposureTime"], metadata["Lux"])
camera.stop()

There are the three usual constructors — create_preview_configuration(), create_still_configuration() and create_video_configuration() — and configure() also accepts the strings "preview", "still" and "video". set_controls() updates the camera's controls. capture_metadata() returns a small dict with SensorTimestamp (nanoseconds on the simulated clock), ExposureTime, AnalogueGain, Lux and ColourTemperature.

The high-level one-liner works too:

Picamera2().start_and_capture_file("quick.jpg", delay=1)

Going deeper

The simulated camera is a Sony IMX708 with a 4608×2592 sensor. Frames are rendered by the page at up to 1920×1080; a larger requested size (the still configuration's full sensor size, for example) is scaled down keeping the aspect ratio, so the saved 12 MP "still" is really 1920×1080. The array format follows the stream configuration: the default preview is XBGR8888 (RGBA), BGR888 gives [R, G, B] and RGB888 gives [B, G, R], matching Picamera2's little-endian naming convention.

start_preview() and stop_preview() do nothing: there is no window in the simulator, and the camera's picture source (a test scene, the webcam, or an image) is chosen where the camera is configured in Lab mode. Starting the camera also lights the module's activity LED in the board view.

Simulator vs real hardware

  • Frames are rendered, not photographed. The page draws the picture the camera "sees" from the configured source — the same scene at every sensor mode. A real sensor would expose RAW modes, autofocus, HDR and rotation; libcamera.Transform is stored but does not transform pixels, and controls like AfMode/AwbMode are accepted as values only.
  • Only Picamera2 and Preview are provided. There is no capture_request loop, no MJPEGEncoder/H264Encoder, no FileOutput, no start_recording() and no multi-camera synchronisation; Preview.NULL/DRM/QT/QTGL constants exist for imports. global_camera_info() always reports the one IMX708 even when nothing is wired — use Picamera2() itself to check the ribbon.
  • A missing camera fails like hardware. Picamera2() without a camera on the CSI connector raises IndexError: list index out of range.
  • Files land in the run's home folder. Photos go to /home/pi and appear in the Lab's Files panel; so do file-like objects passed to capture_file(). Each Run is a fresh Pi, so photos do not survive to the next run.
  • In judged runs the camera is a grey frame. When your code is checked by the judge ("Run tests", contest submissions) the page is not there to draw anything: capture_array() returns an all-128 RGBA field and capture_file() saves a minimal JPEG (SOI/EOI markers). Objective tests must therefore not rely on pixel values; in the sandbox and Lab the frame is a real rendered image.