Depth Post-Processing¶
This example shows how you can run depth post-processing filters on the device itself to reduce noise, smooth the depth map and overall improve the depth map quality. Post-processing can be added to StereoDepth node.
Demo¶
Depth filters¶
This is a non-edge preserving Median filter, which can be used to reduce noise and smoothen the depth map. Median filter is implemented in hardware, so it’s the fastest filter.
-
enum
dai
::
MedianFilter
Median filter config
Values:
-
enumerator
MEDIAN_OFF
-
enumerator
KERNEL_3x3
-
enumerator
KERNEL_5x5
-
enumerator
KERNEL_7x7
-
enumerator
Speckle Filter is used to reduce the speckle noise. Speckle noise is a region with huge variance between neighboring disparity/depth pixels, and speckle filter tries to filter this region.
-
struct
dai::RawStereoDepthConfig::PostProcessing
::
SpeckleFilter
Speckle filtering. Removes speckle noise.
Public Members
-
bool
enable
= false Whether to enable or disable the filter.
-
std::uint32_t
speckleRange
= 50 Speckle search range.
-
bool
Temporal Filter is intended to improve the depth data persistency by manipulating per-pixel values based on previous frames. The filter performs a single pass on the data, adjusting the depth values while also updating the tracking history. In cases where the pixel data is missing or invalid, the filter uses a user-defined persistency mode to decide whether the missing value should be rectified with stored data. Note that due to its reliance on historic data the filter may introduce visible blurring/smearing artifacts, and therefore is best-suited for static scenes.
-
struct
dai::RawStereoDepthConfig::PostProcessing
::
TemporalFilter
Temporal filtering with optional persistence.
Public Types
-
enum
PersistencyMode
Persistency algorithm type.
Values:
-
enumerator
PERSISTENCY_OFF
-
enumerator
VALID_8_OUT_OF_8
-
enumerator
VALID_2_IN_LAST_3
-
enumerator
VALID_2_IN_LAST_4
-
enumerator
VALID_2_OUT_OF_8
-
enumerator
VALID_1_IN_LAST_2
-
enumerator
VALID_1_IN_LAST_5
-
enumerator
VALID_1_IN_LAST_8
-
enumerator
PERSISTENCY_INDEFINITELY
-
enumerator
Public Members
-
bool
enable
= false Whether to enable or disable the filter.
-
PersistencyMode
persistencyMode
= PersistencyMode::VALID_2_IN_LAST_4 Persistency mode. If the current disparity/depth value is invalid, it will be replaced by an older value, based on persistency mode.
-
float
alpha
= 0.4f The Alpha factor in an exponential moving average with Alpha=1 - no filter. Alpha = 0 - infinite filter. Determines the extent of the temporal history that should be averaged.
-
std::int32_t
delta
= 0 Step-size boundary. Establishes the threshold used to preserve surfaces (edges). If the disparity value between neighboring pixels exceed the disparity threshold set by this delta parameter, then filtering will be temporarily disabled. Default value 0 means auto: 3 disparity integer levels. In case of subpixel mode it’s 3*number of subpixel levels.
-
enum
Spatial Edge-Preserving Filter will fill invalid depth pixels with valid neighboring depth pixels. It performs a series of 1D horizontal and vertical passes or iterations, to enhance the smoothness of the reconstructed data. It is based on this research paper.
-
struct
dai::RawStereoDepthConfig::PostProcessing
::
SpatialFilter
1D edge-preserving spatial filter using high-order domain transform.
Public Members
-
bool
enable
= false Whether to enable or disable the filter.
-
std::uint8_t
holeFillingRadius
= 2 An in-place heuristic symmetric hole-filling mode applied horizontally during the filter passes. Intended to rectify minor artefacts with minimal performance impact. Search radius for hole filling.
-
float
alpha
= 0.5f The Alpha factor in an exponential moving average with Alpha=1 - no filter. Alpha = 0 - infinite filter. Determines the amount of smoothing.
-
std::int32_t
delta
= 0 Step-size boundary. Establishes the threshold used to preserve “edges”. If the disparity value between neighboring pixels exceed the disparity threshold set by this delta parameter, then filtering will be temporarily disabled. Default value 0 means auto: 3 disparity integer levels. In case of subpixel mode it’s 3*number of subpixel levels.
-
std::int32_t
numIterations
= 1 Number of iterations over the image in both horizontal and vertical direction.
-
bool
Threshold Filter filters out all disparity/depth pixels outside the configured min/max threshold values.
-
class
depthai.RawStereoDepthConfig.PostProcessing.
ThresholdFilter
Decimation Filter will sub-samples the depth map, which means it reduces the depth scene complexity and allows
other filters to run faster. Setting decimationFactor
to 2 will downscale 1280x800 depth map to 640x400.
-
struct
dai::RawStereoDepthConfig::PostProcessing
::
DecimationFilter
Decimation filter. Reduces the depth scene complexity. The filter runs on kernel sizes [2x2] to [8x8] pixels.
Public Types
-
enum
DecimationMode
Decimation algorithm type.
Values:
-
enumerator
PIXEL_SKIPPING
-
enumerator
NON_ZERO_MEDIAN
-
enumerator
NON_ZERO_MEAN
-
enumerator
Public Members
-
std::uint32_t
decimationFactor
= 1 Decimation factor. Valid values are 1,2,3,4. Disparity/depth map x/y resolution will be decimated with this value.
-
DecimationMode
decimationMode
= DecimationMode::PIXEL_SKIPPING Decimation algorithm type.
-
enum
Similar samples:
Setup¶
Please run the install script to download all required dependencies. Please note that this script must be ran from git context, so you have to download the depthai-python repository first and then run the script
git clone https://github.com/luxonis/depthai-python.git
cd depthai-python/examples
python3 install_requirements.py
For additional information, please follow installation guide
Source code¶
Also available on GitHub
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | #!/usr/bin/env python3
import cv2
import depthai as dai
import numpy as np
# Closer-in minimum depth, disparity range is doubled (from 95 to 190):
extended_disparity = False
# Better accuracy for longer distance, fractional disparity 32-levels:
subpixel = False
# Better handling for occlusions:
lr_check = True
# Create pipeline
pipeline = dai.Pipeline()
# Define sources and outputs
monoLeft = pipeline.create(dai.node.MonoCamera)
monoRight = pipeline.create(dai.node.MonoCamera)
depth = pipeline.create(dai.node.StereoDepth)
xout = pipeline.create(dai.node.XLinkOut)
xout.setStreamName("disparity")
# Properties
monoLeft.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
monoLeft.setCamera("left")
monoRight.setResolution(dai.MonoCameraProperties.SensorResolution.THE_400_P)
monoRight.setCamera("right")
# Create a node that will produce the depth map (using disparity output as it's easier to visualize depth this way)
depth.setDefaultProfilePreset(dai.node.StereoDepth.PresetMode.HIGH_DENSITY)
# Options: MEDIAN_OFF, KERNEL_3x3, KERNEL_5x5, KERNEL_7x7 (default)
depth.initialConfig.setMedianFilter(dai.MedianFilter.KERNEL_7x7)
depth.setLeftRightCheck(lr_check)
depth.setExtendedDisparity(extended_disparity)
depth.setSubpixel(subpixel)
config = depth.initialConfig.get()
config.postProcessing.speckleFilter.enable = False
config.postProcessing.speckleFilter.speckleRange = 50
config.postProcessing.temporalFilter.enable = True
config.postProcessing.spatialFilter.enable = True
config.postProcessing.spatialFilter.holeFillingRadius = 2
config.postProcessing.spatialFilter.numIterations = 1
config.postProcessing.thresholdFilter.minRange = 400
config.postProcessing.thresholdFilter.maxRange = 15000
config.postProcessing.decimationFilter.decimationFactor = 1
depth.initialConfig.set(config)
# Linking
monoLeft.out.link(depth.left)
monoRight.out.link(depth.right)
depth.disparity.link(xout.input)
# Connect to device and start pipeline
with dai.Device(pipeline) as device:
# Output queue will be used to get the disparity frames from the outputs defined above
q = device.getOutputQueue(name="disparity", maxSize=4, blocking=False)
while True:
inDisparity = q.get() # blocking call, will wait until a new data has arrived
frame = inDisparity.getFrame()
# Normalization for better visualization
frame = (frame * (255 / depth.initialConfig.getMaxDisparity())).astype(np.uint8)
cv2.imshow("disparity", frame)
# Available color maps: https://docs.opencv.org/3.4/d3/d50/group__imgproc__colormap.html
frame = cv2.applyColorMap(frame, cv2.COLORMAP_JET)
cv2.imshow("disparity_color", frame)
if cv2.waitKey(1) == ord('q'):
break
|
Also available on GitHub
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | #include <iostream>
// Inludes common necessary includes for development using depthai library
#include "depthai/depthai.hpp"
// Closer-in minimum depth, disparity range is doubled (from 95 to 190):
static std::atomic<bool> extended_disparity{false};
// Better accuracy for longer distance, fractional disparity 32-levels:
static std::atomic<bool> subpixel{false};
// Better handling for occlusions:
static std::atomic<bool> lr_check{true};
int main() {
// Create pipeline
dai::Pipeline pipeline;
// Define sources and outputs
auto monoLeft = pipeline.create<dai::node::MonoCamera>();
auto monoRight = pipeline.create<dai::node::MonoCamera>();
auto depth = pipeline.create<dai::node::StereoDepth>();
auto xout = pipeline.create<dai::node::XLinkOut>();
xout->setStreamName("disparity");
// Properties
monoLeft->setResolution(dai::MonoCameraProperties::SensorResolution::THE_400_P);
monoLeft->setCamera("left");
monoRight->setResolution(dai::MonoCameraProperties::SensorResolution::THE_400_P);
monoRight->setCamera("right");
// Create a node that will produce the depth map (using disparity output as it's easier to visualize depth this way)
depth->setDefaultProfilePreset(dai::node::StereoDepth::PresetMode::HIGH_DENSITY);
// Options: MEDIAN_OFF, KERNEL_3x3, KERNEL_5x5, KERNEL_7x7 (default)
depth->initialConfig.setMedianFilter(dai::MedianFilter::KERNEL_7x7);
depth->setLeftRightCheck(lr_check);
depth->setExtendedDisparity(extended_disparity);
depth->setSubpixel(subpixel);
auto config = depth->initialConfig.get();
config.postProcessing.speckleFilter.enable = false;
config.postProcessing.speckleFilter.speckleRange = 50;
config.postProcessing.temporalFilter.enable = true;
config.postProcessing.spatialFilter.enable = true;
config.postProcessing.spatialFilter.holeFillingRadius = 2;
config.postProcessing.spatialFilter.numIterations = 1;
config.postProcessing.thresholdFilter.minRange = 400;
config.postProcessing.thresholdFilter.maxRange = 15000;
config.postProcessing.decimationFilter.decimationFactor = 1;
depth->initialConfig.set(config);
// Linking
monoLeft->out.link(depth->left);
monoRight->out.link(depth->right);
depth->disparity.link(xout->input);
// Connect to device and start pipeline
dai::Device device(pipeline);
// Output queue will be used to get the disparity frames from the outputs defined above
auto q = device.getOutputQueue("disparity", 4, false);
while(true) {
auto inDepth = q->get<dai::ImgFrame>();
auto frame = inDepth->getFrame();
// Normalization for better visualization
frame.convertTo(frame, CV_8UC1, 255 / depth->initialConfig.getMaxDisparity());
cv::imshow("disparity", frame);
// Available color maps: https://docs.opencv.org/3.4/d3/d50/group__imgproc__colormap.html
cv::applyColorMap(frame, frame, cv::COLORMAP_JET);
cv::imshow("disparity_color", frame);
int key = cv::waitKey(1);
if(key == 'q' || key == 'Q') {
return 0;
}
}
return 0;
}
|