raspberry-pi intermediate 30 min

Raspberry Pi: use the Camera Module v3 with libcamera and Picamera2

Wire a Camera Module v3 to a Raspberry Pi, capture stills and video from the command line with libcamera, and write Python with the modern Picamera2 library.

Code available for: Python
Published Aug 26, 2026

The Raspberry Pi Camera Module v3 is a 12-megapixel camera that costs about $35, plugs into the Pi’s CSI port, and runs on every Pi from the Zero 2 W to the Pi 5. It is the camera I reach for when I need “a real camera” for a project, not a USB webcam.

The software side changed a few years ago. The old raspistill and raspivid commands are deprecated. The new system is libcamera, with command-line tools (libcamera-still, libcamera-vid) and a Python library called picamera2. This tutorial covers the modern stack.

What you need

  • Raspberry Pi 4 or Pi 5 (Pi 3 works but is slow; Pi Zero 2 W works but is slow for video)
  • Camera Module v3 (the v2 still works with this tutorial, the v3 has autofocus)
  • The ribbon cable that ships with the camera
  • Raspberry Pi OS Bookworm (64-bit) with desktop, or Lite plus libcamera-apps and python3-picamera2

The legacy raspistill vs current libcamera

The old way was the raspistill and raspivid commands, which used Broadcom’s proprietary GPU code. The new way is libcamera, an open-source stack that works on more hardware and is not tied to the Pi.

If you see a tutorial from before 2022, it will say raspistill. That command is gone. The replacement is libcamera-still with mostly the same flags.

# Old
raspistill -o image.jpg

# New
libcamera-still -o image.jpg

The flag names are similar. The image quality is similar. The internal plumbing is completely different.

Install libcamera

On a fresh Raspberry Pi OS Bookworm install, libcamera-apps is already installed. If not:

sudo apt update
sudo apt install -y libcamera-apps python3-picamera2

Verify:

libcamera-hello

You should see a preview window for 5 seconds (on a desktop install) or a confirmation message (on a Lite install).

The camera interface is enabled by default on recent Raspberry Pi OS. If libcamera-hello says “no cameras found,” enable the interface with sudo raspi-config >> Interface Options >> Camera >> Yes, then reboot.

Wiring: the ribbon cable

The Camera Module has a ribbon cable that plugs into the CSI port on the Pi. The port is between the HDMI ports and the GPIO header on a Pi 4, and near the GPIO header on a Pi 5.

The cable is fragile. The two things that go wrong:

  1. The cable is upside down. The blue side of the cable should face the Ethernet ports (Pi 4) or the board (Pi 5). If it is facing the other way, the camera is not detected.
  2. The cable is not fully inserted. The connector is a friction lock. Push the cable in until the lock clicks. If you have to force it, the cable is in the wrong orientation.

The “no camera detected” error is almost always one of those two things. Verify with:

libcamera-hello --list-cameras

If the camera is detected, the output shows the sensor model and capabilities.

Still capture

libcamera-still -o image.jpg

This captures a single still image. Default resolution is the camera’s maximum (12 MP for v3, 8 MP for v2). The image saves to the specified file.

Useful flags:

  • -o image.jpg output file
  • --width 1920 --height 1080 specific resolution
  • --rotation 180 rotate (mounting orientation)
  • --quality 90 JPEG quality (0-100)
  • --awb auto auto white balance
  • --exposure sport exposure mode (long, normal, short, sport)

For timelapse:

libcamera-still -o frame_%04d.jpg --timelapse 5000 --timeout 60000

Captures one frame every 5 seconds for 60 seconds.

Video capture

libcamera-vid -o video.h264

This captures 1080p30 video in H.264 format. H.264 is the right format if you are going to play it back or upload it; the file size is reasonable.

Useful flags:

  • -o video.h264 output file
  • --width 1920 --height 1080 resolution
  • --framerate 30 frames per second
  • --bitrate 8000000 8 Mbps (default 17 Mbps for 1080p)
  • --timeout 10000 stop after 10 seconds

To convert H.264 to MP4 (most players want the container):

ffmpeg -i video.h264 -c copy video.mp4

The Picamera2 Python library

picamera2 is the Python library that replaces the old picamera. The old library does not work on Bookworm and the new Pi models. The new library does.

from picamera2 import Picamera2

picam2 = Picamera2()
config = picam2.create_still_configuration()
picam2.configure(config)
picam2.start()

# Let auto-exposure settle
import time
time.sleep(2)

picam2.capture_file("image.jpg")

The time.sleep(2) is the part most people skip. The camera’s auto-exposure needs a couple of seconds to settle. Without it, the first frame is under- or over-exposed.

For a preview loop:

import time
from picamera2 import Picamera2

picam2 = Picamera2()
config = picam2.create_preview_configuration()
picam2.configure(config)
picam2.start()

while True:
    frame = picam2.capture_array()
    # frame is a numpy array, shape (height, width, 3)
    # process or display it
    time.sleep(0.1)

The capture_array method returns the image as a numpy array. This is the right method if you are doing image processing (OpenCV, Pillow, scikit-image).

The autofocus trick (v3 only)

The Camera Module v3 has autofocus. The v2 has a fixed focus lens. The v3’s autofocus is controlled from Picamera2:

from picamera2 import Picamera2
import time

picam2 = Picamera2()
config = picam2.create_still_configuration()
picam2.configure(config)
picam2.start()

# Continuous autofocus
picam2.set_controls({"AfMode": 2})   # 2 = continuous, 1 = manual, 0 = off

time.sleep(5)  # let it focus
picam2.capture_file("focused.jpg")

The autofocus is slow (about 1-2 seconds to lock). For a project that needs fast capture, set the focus manually:

# Manual focus at a specific lens position (0.0 = far, 10.0 = close)
picam2.set_controls({"AfMode": 0, "LensPosition": 5.0})

The v3 also has a wider field of view than the v2 (about 66 degrees horizontal vs 62), and it supports HDR. For most projects, the autofocus alone is the reason to pick the v3.

Exposure modes and AWB

The camera has two auto-adjustment systems running in parallel:

  • Exposure (the brightness of the image). The camera picks a shutter speed and ISO to make the image well-exposed.
  • AWB (auto white balance). The camera picks color gains so that white things look white under the current lighting.

Both are “auto” by default. You can override:

picam2.set_controls({
    "ExposureTime": 10000,        # 10 ms in microseconds
    "AnalogueGain": 1.0,          # ISO 100 equivalent
    "ColourGains": (1.5, 1.2),   # red gain, blue gain
})

The values are tricky to pick by hand. The rule: leave the camera in auto mode, override only if the auto is consistently wrong (a window scene with a bright sky, a lab with fluorescent lights, etc.).

Streaming over RTSP

For “show the camera feed to multiple viewers,” RTSP is the standard protocol. There is a libcamera-vid mode for it:

libcamera-vid -t 0 --inline -o - | cvlc stream:///dev/stdin --sout '#rtp{sdp=rtsp://:8554/stream}' :demux=h264

This is more involved than I want to put in a beginner tutorial. The short version: VLC, GStreamer, or mediaMTX (formerly rtsp-simple- server) can all take the H.264 stream from libcamera-vid and expose it as RTSP. The viewers use VLC or a web player.

For a simpler “view the camera in a browser,” use rpicam-vid (the newer name for libcamera-vid) with the --inline and HTTP serving. Or skip the streaming and just push JPEGs to a web server with a Python script.

ESP32-CAM vs Pi Camera

The ESP32-CAM is a $10 board with an OV2640 camera. It runs over Wi-Fi, draws very little power, and is the right pick for a battery- powered, always-on camera.

The Pi Camera wins on:

  • Image quality. The v3 is 12 MP, the OV2640 is 2 MP.
  • Autofocus. The v3 has it, the OV2640 does not.
  • Processing. The Pi can do real-time image processing (face detection, object recognition). The ESP32-CAM struggles.
  • Flexibility. The Pi runs full Linux, you can install any library.

The ESP32-CAM wins on:

  • Cost. About $10 vs $35 for the camera, plus the Pi.
  • Power. 200 mA vs 600-1000 mA for a Pi.
  • Integration. The ESP32-CAM has the camera on the same board as the microcontroller.

Rule of thumb: if the camera is the main thing and the project runs on battery, ESP32-CAM. If the camera is one of several things and the Pi is already there, Pi Camera.

What you learned

  • The modern camera stack on the Pi is libcamera (CLI tools) and picamera2 (Python).
  • The Camera Module v3 has autofocus; the v2 has a fixed-focus lens.
  • libcamera-still for photos, libcamera-vid for video, picamera2 for Python.
  • The “no camera detected” error is almost always a ribbon cable orientation problem.

When something breaks

libcamera-hello says “no cameras available”. The ribbon cable is upside down or not fully inserted. Power off, reseat the cable.

The preview is green or pink. The cable is not fully inserted or the cable is damaged. Try a different cable.

The autofocus hunts forever. The lens is too close to the subject or the lighting is too low. Use manual focus with LensPosition.

ModuleNotFoundError: No module named 'picamera2'. The package is not installed. Run sudo apt install -y python3-picamera2.

The image is upside down. The camera is mounted upside down. Add picam2.set_controls({"Rotation": 180}) or use the --rotation 180 flag on the CLI tools.

The Python script crashes after a few minutes. The camera is running out of resources. Add picam2.close() at the end of the script, or use a context manager:

with Picamera2() as picam2:
    picam2.start()
    picam2.capture_file("image.jpg")

What to build next

  • A wildlife camera with motion detection.
  • A time-lapse rig for a plant growing.
  • A security camera with RTSP streaming.
  • A doorbell camera that sends a snapshot to your phone when pressed.

The time-lapse rig is the easiest first project. The RTSP security camera is the most useful.