Basic Image Capture (JPEG, PNG)
Relevant source files
The following files were used as context for generating this wiki page:
The picamera2 library provides a powerful and flexible API for capturing images from Raspberry Pi camera modules. This documentation covers the fundamental workflows for capturing still images and saving them in common formats like JPEG and PNG. It explores the core components involved, from camera configuration to handling the final image data.
The primary mechanism involves configuring the camera for still capture, starting the camera, issuing a capture request, and then processing the resulting CompletedRequest object to save the image to a file. The library offers both high-level convenience functions for simple cases and lower-level control for more advanced scenarios.
Core Capture Workflow
The process of capturing a still image generally follows a set sequence of steps. The library provides helper methods to simplify this, but understanding the underlying flow is crucial for customization.
The diagram below illustrates the typical lifecycle for a single image capture.
1. Initialization and Configuration
First, a Picamera2 object is instantiated. Before starting the camera, it must be configured. For still images, create_still_configuration() is typically used. This method generates a suitable configuration dictionary that can be customized.
# examples/capture_jpeg.py:10-12
picam2 = Picamera2()
config = picam2.create_still_configuration()
picam2.configure(config)
The configuration can specify multiple streams, such as main for the full-resolution image and lores for a smaller, lower-resolution version, which might be used for display or faster processing.
Sources: examples/capture_jpeg.py:10-12, apps/app_full.py:91-94
2. Capturing the Image
Once the camera is started with picam2.start(), an image can be captured.
High-Level Capture
For the simplest use case, capture_file() configures, starts, captures, and stops the camera in one call.
# examples/easy_capture.py:7
picam2.start_and_capture_file("easy_capture.jpg", delay=0, show_preview=True)
Sources: examples/easy_capture.py:7
Low-Level Capture
For more control, capture_request() is used. This is an asynchronous call that returns a "job". The application must then wait for the job to complete using picam2.wait(job), which returns a CompletedRequest object. This object holds the image buffer and metadata.
# apps/app_full.py:230-231
picam2.capture_request(signal_function=qpicamera2.signal_done)
# apps/app_full.py:270-272
def capture_done(job):
request = picam2.wait(job)
Sources: apps/app_full.py:230-231, 270-272
The following sequence diagram shows the interaction for a low-level capture.
The CompletedRequest Object
The CompletedRequest class is a central component for accessing captured data. It acts as a wrapper around the buffers and metadata returned by libcamera for a completed frame. It is designed to be reference-counted; once all references are released, the underlying buffers are returned to the camera system for reuse.
Key methods of CompletedRequest:
| Method | Description |
|---|---|
get_metadata() | Returns a dictionary of the frame's metadata (e.g., ExposureTime, AnalogueGain). |
make_buffer(name) | Returns a 1D NumPy array of the raw buffer data for a named stream. |
make_array(name) | Returns a 2D/3D NumPy array representing the image from a named stream. This involves a data copy. |
make_image(name) | Creates a Pillow (PIL) Image object from a named stream. |
save(name, file_output, format) | Saves the image from a named stream to a file or file-like object. |
save_dng(file_output, name) | Saves the raw stream buffer as a DNG file. |
release() | Decrements the reference count, releasing the request back to the camera when the count reaches zero. |
Sources: picamera2/request.py:90-341
Saving Images
The CompletedRequest.save() method is the primary way to persist a captured frame to a file. It contains optimized paths for different formats.
Saving as JPEG
The library includes a highly optimized path for saving JPEGs, which is enabled by default via the CompletedRequest.FASTER_JPEG = True flag.
This logic is shown in the flowchart below:
Sources: picamera2/request.py:277-326
- Optimized Path: If the stream format is
YUV420or theFASTER_JPEGflag is on, thesimplejpeglibrary is used to perform a fast encoding. This is particularly efficient forYUV420as it avoids a full color conversion. For other RGB-like formats (XBGR8888,BGR888, etc.),simplejpegis still used for speed. - EXIF Data: When saving a JPEG, the
savemethod automatically prepares and injects EXIF data from the request's metadata. This includes camera model, exposure time, and gain. - Fallback Path: If the conditions for the fast path are not met, the method falls back to creating a PIL Image via
make_image()and then usingimg.save().
Sources: picamera2/request.py:277-318
Saving as PNG and Other Formats
When saving to formats like PNG, BMP, or GIF, the save() method internally uses make_image() to create a PIL Image object and then calls the save() method on that object. This relies on Pillow's encoding capabilities.
# examples/capture_png.py:12
picam2.start()
time.sleep(2)
# examples/capture_png.py:16
picam2.capture_file("test.png")
The capture_file convenience function determines the format from the file extension and calls CompletedRequest.save() internally.
Sources: examples/capture_png.py:12-16, picamera2/request.py:323-326
Saving as DNG (RAW)
The library also supports saving raw sensor data in the DNG format. This is handled by the dedicated save_dng() method, which uses the PiDNG library. This requires a raw stream to be configured.
# apps/app_full.py:275-277
if pic_tab.filetype.currentText() == "raw":
request.save_dng(
f"{pic_tab.filename.text() if pic_tab.filename.text() else 'test'}.dng"
)
Sources: apps/app_full.py:275-277, picamera2/request.py:328-334, examples/capture_video_raw.py:44-60
Configuration for Still Capture
Properly configuring the camera is essential for getting the desired image. The create_still_configuration() method is the recommended starting point.
| Parameter | Type | Description | Example |
|---|---|---|---|
main | dict | Configuration for the main, high-resolution stream. Can specify size and format. | main={"size": (1920, 1080)} |
lores | dict | Configuration for a secondary, low-resolution stream. | lores={"size": (640, 480)} |
raw | dict | Configuration for the raw sensor stream. Required for DNG output. | raw={"size": picam2.sensor_resolution} |
display | str | The name of the stream to use for preview windows. | display="lores" |
encode | str | The name of the stream to use for video encoding. | encode="lores" |
colour_space | libcamera.ColorSpace | Sets the color space for the capture (e.g., Sycc, Rec709). | colour_space=ColorSpace.Sycc() |
Sources: apps/app_full.py:91-94, tests/colour_spaces.py:49-51
A typical still configuration might request a full-resolution main stream and a smaller lores stream. The lores stream can be used for a preview window without the performance overhead of displaying the full-resolution image.
# apps/app_full.py:89-94
lores_size = picam2.sensor_resolution
while lores_size[0] > 1600:
lores_size = (lores_size[0] // 2 & ~1, lores_size[1] // 2 & ~1)
still_kwargs = {"lores": {"size": lores_size}, "display": "lores", "encode": "lores", "buffer_count": 1}
picam2.still_configuration = picam2.create_still_configuration(
**still_kwargs,
)
Sources: apps/app_full.py:89-94
Summary
The picamera2 library provides a tiered API for basic image capture. Simple, one-line commands like start_and_capture_file are available for straightforward tasks. For more complex needs, the capture_request and CompletedRequest workflow offers fine-grained control over the capture process, data access, and saving mechanism. The library is optimized for common formats like JPEG while retaining the flexibility to handle raw data and other standard image types through integration with libraries like simplejpeg and Pillow.