Request and Metadata Handling
Relevant source files
The following files were used as context for generating this wiki page:The picamera2 library provides a robust mechanism for handling camera captures through a system of requests. When a frame is captured, it is returned to the user application encapsulated within a CompletedRequest object. This object is the primary interface for accessing not only the image data from various streams (e.g., main, lores, raw) but also a rich set of metadata that describes the state of the camera sensor and image processing pipeline at the moment of capture.
Understanding how to manage CompletedRequest objects and interpret their metadata is crucial for developing advanced applications. This includes tasks like saving images with correct EXIF information, performing computer vision tasks that rely on camera parameters, or synchronizing captures with external events. The library supports both synchronous and asynchronous request handling, allowing for flexible and efficient application design.
Sources: picamera2/request.py, picamera2/picamera2.py
The CompletedRequest Object
The CompletedRequest class is a wrapper around a completed libcamera request. It holds the image buffers for all configured streams and the corresponding metadata. It is designed to be reference-counted to manage memory efficiently.
Sources: picamera2/request.py:86-91
Class Structure
The diagram below shows the key components of the CompletedRequest class.
This diagram illustrates the main methods and properties of the CompletedRequest class for accessing data and managing its lifecycle.
Sources: picamera2/request.py:86-311
Lifecycle: Acquire and Release
To prevent the camera system from recycling a request's buffers while they are still in use, CompletedRequest uses a reference counting mechanism.
- Creation: A
CompletedRequestis created by thePicamera2internal event loop whenlibcamerasignals a request is complete. It starts with a reference count of 1. - Acquisition: When an application gets a
CompletedRequest(e.g., viacapture_request()), it "owns" that reference. If the application needs to hold on to the request for longer, it can callacquire()to increment the reference count. - Release: When the application is finished with the request, it must call
release(). This decrements the reference count. When the count reaches zero, the request and its buffers are returned to the camera system for reuse. Failing to release a request will lead to a stall in the camera pipeline as it will run out of buffers.
The picam2.captured_request() context manager is the recommended way to ensure release() is always called.
# Using a context manager to ensure release
with picam2.captured_request() as request:
# do something with the request
metadata = request.get_metadata()
# request.release() is called automatically on exit
Sources: picamera2/picamera2.py:1584-1590, picamera2/request.py:120-168
Request Capture Flow
Requests can be captured synchronously or asynchronously. Asynchronous capture is more efficient as it doesn't block the main thread.
Asynchronous Capture
The following sequence diagram shows the process for an asynchronous capture, which returns a Job object that can be waited upon.
This diagram shows how an application initiates a non-blocking capture and later retrieves the result.
Sources: picamera2/picamera2.py:1642-1655, picamera2/picamera2.py:1455-1476
Synchronous Capture
A synchronous call (wait=True or by default) is a simpler pattern where the capture_request method blocks until the CompletedRequest is available and returns it directly. This is essentially a combination of the asynchronous call and the wait() call.
Sources: picamera2/picamera2.py:1469-1476
Flushing the Pipeline
The capture_request method accepts a flush parameter.
flush=True: Ensures that the returned request did not start being exposed before thecapture_requestcall was made. This is useful for capturing an event that has just happened.flush=<timestamp>: Ensures the returned request did not start being exposed before the given monotonic timestamp (in nanoseconds). This is useful for scheduling a capture in the near future.
# Capture a request that starts no earlier than now
check_time = time.monotonic_ns()
request = picam2.capture_request(flush=True)
# Capture a request that starts no earlier than 0.5s from now
future_time = time.monotonic_ns() + 5e8
request = picam2.capture_request(flush=future_time)
Sources: examples/timestamp_capture.py:18-39, picamera2/picamera2.py:1653-1654
Accessing Data and Metadata
Once you have a CompletedRequest object, you can access its image data and metadata.
Image Data
The CompletedRequest class provides several methods to access the image data in different formats.
| Method | Return Type | Description |
|---|---|---|
make_buffer(name) | numpy.ndarray | Returns a 1D NumPy array containing the raw buffer data for the named stream. This is the most direct, low-level access. |
make_array(name) | numpy.ndarray | Returns a 2D/3D NumPy array representing the image. This involves reshaping the buffer and handling stride. A copy of the data is made. |
make_image(name) | PIL.Image.Image | Returns a Pillow (PIL) Image object. Convenient for image manipulation and saving in various standard formats. |
Sources: picamera2/request.py:170-252
Metadata
The get_metadata() method returns a dictionary containing a wealth of information about the frame. This data is sourced directly from libcamera.
# Example of capturing and printing metadata
from picamera2 import Picamera2
picam2 = Picamera2()
picam2.start()
metadata = picam2.capture_metadata()
for key, value in metadata.items():
print(f"{key}: {value}")
picam2.stop()
Sources: examples/metadata.py, picamera2/request.py:182-188
The post_callback on the Picamera2 object can be used to inspect metadata for every single frame that the camera produces, which is useful for debugging or continuous monitoring.
# Example from apps/app_full.py
def post_callback(request):
metadata = request.get_metadata()
# ... process metadata ...
info_tab.setText('\n'.join(pretty_metadata))
picam2.post_callback = post_callback
Sources: apps/app_full.py:27-51, apps/app_full.py:55
Common Metadata Fields
The following table lists some of the most common and useful metadata fields.
| Metadata Key | Type | Description |
|---|---|---|
SensorTimestamp | int | Timestamp in nanoseconds from the sensor when the first pixel of the frame was read out. |
ExposureTime | int | The exposure time of the frame in microseconds. |
AnalogueGain | float | The analogue gain applied to the frame. |
DigitalGain | float | The digital gain applied to the frame. |
ColourGains | tuple[float, float] | The red and blue colour gains applied by the AWB algorithm. |
ColourTemperature | int | The correlated colour temperature in Kelvin estimated by the AWB algorithm. |
ScalerCrop | tuple[int, int, int, int] | The crop rectangle (x, y, width, height) applied by the sensor's scaler. |
FrameDuration | int | The actual duration of the frame in microseconds. |
AfState | Enum | The current state of the autofocus algorithm (e.g., Idle, Scanning, Focused). |
Sources: picamera2/request.py:512-519, apps/app_full.py:44-50, examples/timestamp_capture.py:11-12
Saving Files and EXIF Data
The CompletedRequest object can save its data directly to a file or a file-like object.
save(name, file_output, format): Saves the specified stream as a JPEG or PNG.save_dng(file_output): Saves therawstream as a DNG file.
When saving a JPEG, picamera2 automatically generates and includes EXIF data based on the request's metadata. This includes fields like ExposureTime, ISOSpeedRatings (calculated from total gain), Model, and DateTime.
The flow for saving a file is shown below. Note the optimized path for certain JPEG encodings.
This diagram shows the logic within the save method, highlighting the performance optimization for common JPEG formats.
Sources: picamera2/request.py:254-311
An application can also provide its own custom EXIF data, which will be merged with the automatically generated tags.
# Example of adding custom EXIF data
import piexif
exif_data = {"0th": {piexif.ImageIFD.Copyright: b"Copyright (c) 2023, Me"}}
picam2.capture_file("test.jpg", exif_data=exif_data)
Sources: picamera2/request.py:254-261, picamera2/request.py:512-523
The Metadata Class
For developers who prefer an object-oriented style for accessing metadata, the library provides a simple Metadata wrapper class. This class takes the metadata dictionary and makes each key available as an attribute.
from picamera2.metadata import Metadata
# dict_metadata is the dictionary from request.get_metadata()
md = Metadata(dict_metadata)
# Access values as attributes
print("Exposure time:", md.ExposureTime)
This is purely a convenience wrapper and its use is optional. Most examples and internal code handle the metadata as a standard Python dictionary.
Sources: picamera2/metadata.py
Summary
The CompletedRequest object is a cornerstone of the picamera2 library, providing a unified interface to access both image data and the rich metadata associated with each captured frame. Its reference-counted lifecycle ensures efficient memory management, while the asynchronous capture mechanism allows for high-performance, non-blocking applications. By leveraging the get_metadata() method, developers can gain deep insight into the camera's state, enabling advanced control and image processing techniques.
Sources: picamera2/request.py, picamera2/picamera2.py