Based on contributions by Mathijs.
Once pyrealsense2 is installed (see How to install Intel RealSense librealsense and pyrealsense2 on a Raspberry Pi 5 (Ubuntu) if you’re on a Raspberry Pi), this how-to shows how to grab a depth frame from an Intel RealSense camera, convert it to a normalized, saveable image, and apply Intel’s own depth filters.
What you need
- An Intel RealSense camera and
pyrealsense2installed. pip install opencv-python numpy matplotlib pyexiv2pyexiv2is used to attach metadata to the saved image; on Linux it needs theexiv2development headers available to build/install (for examplesudo apt install libexiv2-dev).
Steps
1. Grab a depth frame and save it
import cv2
import matplotlib.pyplot as plt
import numpy as np
from pyexiv2 import Image
import pyrealsense2 as rs
# Create a pipeline object. This owns the handle to the streaming camera.
pipeline = rs.pipeline()
pipeline.start()
depth = None
i = 0
while not depth:
i += 1
frames = pipeline.wait_for_frames()
depth = frames.get_depth_frame()
print(f"Grabbing frame took this many tries: {i}")
depth_data = depth.as_frame().get_data()
np_image = np.asanyarray(depth_data) # raw sensor values — do not use this directly for real distances
height, width = np_image.shape
cv2.imwrite("testimg.png", np_image)
# Calculate the real-world distance (in meters) for every pixel
distarray = np.empty([height, width], dtype=np.float64)
for y in range(height):
for x in range(width):
distarray[y, x] = depth.get_distance(x, y)
# Don't call pipeline.stop() before all operations on the depth frame are done —
# it will break get_distance().
pipeline.stop()
max_dist = np.max(distarray)
print(f"max_dist = {max_dist}")
# Normalize the distance array to the full 16-bit range
normalize = lambda t: t / max_dist * 65535 # 16-bit int!
vfunc = np.vectorize(normalize)
normalized_distarray = vfunc(distarray)
# Convert to uint16 to drop the decimals
img = normalized_distarray.astype(np.uint16)
plt.imshow(img, cmap="gray")
# Save the image and store max_dist as metadata — you need it later to convert
# pixel values back into real distances.
filename = "normalized.png"
cv2.imwrite(filename, img)
with Image(filename) as img_meta:
img_meta.modify_xmp({"Xmp.dc.max_dist": f"{max_dist}"})
# Read the metadata back to confirm it was written
with Image(filename) as img_meta:
data = img_meta.read_xmp()
print(data)
The nested per-pixel loop that fills
distarrayis simple but slow for larger images, since it calls into the SDK once per pixel from Python. If this becomes a bottleneck, look intopyrealsense2’s bulk/vectorized distance conversion (via the depth frame’s scale) instead of looping pixel by pixel.
2. Apply depth filters
Intel provides a worked example of RealSense depth filtering on GitHub: depth_filters.ipynb. Download it and run it in Jupyter Notebook.
If you want to apply the filters directly to live frames from a RealSense camera (instead of from a file), use this modified version that reads from the camera: depth_filters.ipynb (10.0 KB)
Troubleshooting
get_distance()returns wrong or zero values after callingpipeline.stop(): make surepipeline.stop()is called only after you’re completely done reading distances from the depth frame, not before.- Metadata step fails: confirm
pyexiv2’s underlyingexiv2library is actually installed on your system, not just the Python package.
Related
Rewritten and consolidated (Sept 2026) from the original student how-to’s: Howto grab, save and filter images with librealsense.