# ToF

The `ToF` node converts raw Time-of-Flight sensor data into depth and exposes both base and filtered outputs. It is available on
RVC2 and RVC4 devices with integrated ToF sensors, such as:

 * [OAK-D ToF](https://shop.luxonis.com/products/oak-d-sr-poe)
 * [OAK-FFC ToF 33D](https://shop.luxonis.com/products/oak-ffc-tof-33d)

ToF depth can be used directly with spatial nodes, for example
[SpatialDetectionNetwork](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/spatial_detection_network.md) and
[SpatialLocationCalculator](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/spatial_location_calculator.md).

For depth quality comparisons with stereo, see [ToF depth
accuracy](https://docs.luxonis.com/hardware/platform/depth/depth-accuracy.md).

## How to place it

#### Python

```python
pipeline = dai.Pipeline()
tof = pipeline.create(dai.node.ToF)
```

#### C++

```cpp
dai::Pipeline pipeline;
auto tof = pipeline.create<dai::node::ToF>();
```

## Inputs and Outputs

 * `rawDepth` and `phase` are RVC2-only outputs.
 * `raw` and `confidence` are RVC4-only outputs.

## ToF Profiles

The recommended way to configure the ToF node is through profiles. A profile sets defaults for a given operating range without
requiring manual tuning of low-level parameters. Profiles are unified across RVC2 and RVC4.

| Profile | Approx. range | Typical use-case |
| --- | --- | --- |
| `LOW_RANGE` | ~10 cm to 1.5 m | Short-range applications such as bin picking |
| `MID_RANGE` (default) | ~10 cm to 5 m | General-purpose / universal profile |
| `HIGH_RANGE` | from ~3 m and further | Long-range sensing |

#### Python

```python
pipeline = dai.Pipeline()

tof = pipeline.create(dai.node.ToF)
tof.build(
    boardSocket=dai.CameraBoardSocket.AUTO,
    profile=dai.ToFConfig.Profile.MID_RANGE,
    fps=FPS,
)
```

#### C++

```cpp
dai::Pipeline pipeline;

auto tof = pipeline.create<dai::node::ToF>()->build(
    dai::CameraBoardSocket::AUTO,
    dai::ToFConfig::Profile::MID_RANGE
);
```

## Architecture and outputs

#### RVC2

On RVC2, the `depth` output is filtered on the host by the ImageFilters node. RVC2 profiles combine the host-side filter presets
with the internal ToF settings. The outputs are:

 * `depth` (uint16, mm)
 * `amplitude` (uint16)
 * `intensity` (uint8)
 * `rawDepth` (uint16, mm)
 * `phase` (float32)

### RVC2 ToF settings

Common base decode settings (from
[`ToFConfig`](https://docs.luxonis.com/software-v3/depthai/depthai-components/messages/tof_config.md)):

 * Phase unwrapping: extends range at the cost of more noise.
 * Phase shuffle temporal filter: reduces noise by combining shuffled/non-shuffled captures.

Approximate range by unwrapping level:

 * `0` (disabled): up to ~1.87 m (80 MHz)
 * `1`: up to ~3.0 m
 * `2`: up to ~4.5 m
 * `3`: up to ~6.0 m
 * `4`: up to ~7.5 m

### RVC2 depth image filters

Post-processing is configured via
[`ImageFilters`](https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes/image_filters.md) and
[`ImageFiltersConfig`](https://docs.luxonis.com/software-v3/depthai/depthai-components/messages/image_filters_config.md). For
confidence-driven cleanup, see
[`ToFDepthConfidenceFilterConfig`](https://docs.luxonis.com/software-v3/depthai/depthai-components/messages/tof_depth_confidence_filter_config.md).

Because these filters run on the host, consider disabling them if processing or bandwidth is a bottleneck. For a deeper look at
the performance impact, see [this blog
post](https://discuss.luxonis.com/blog/6359-new-tof-filters-major-improvements-in-depth-perception-and-point-clouds). To disable
all host-side filters:

```python
filters_cfg = dai.ImageFiltersConfig()
filters_cfg.insertFilter(dai.node.ImageFilters.MedianFilterParams.MEDIAN_OFF)

temporal = dai.node.ImageFilters.TemporalFilterParams()
temporal.enable = False
filters_cfg.insertFilter(temporal)

speckle = dai.node.ImageFilters.SpeckleFilterParams()
speckle.enable = False
filters_cfg.insertFilter(speckle)

spatial = dai.node.ImageFilters.SpatialFilterParams()
spatial.enable = False
filters_cfg.insertFilter(spatial)

filters_cfg_q.send(filters_cfg)
```

#### Quick tuning guide

The tuning goal is to balance a clean, noise-free depth map with fast, accurate updates of moving objects. For ToF depth outputs,
`delta` is typically interpreted in depth units (usually millimeters), while `alpha` is a blending factor in `[0.0, 1.0]`. Treat
all values below as starting points and tune for your scene.

##### Step 1: Blank slate

Before tuning, disable all filters. This helps you measure baseline noise, verify sensor placement/lighting, and avoid masking
setup issues with software filtering.

##### Step 2: Temporal filter (start here)

Tune `TemporalFilterParams` first because it controls dynamic smoothing over time.

 * `delta`: threshold for deciding whether a depth change is significant vs noise. * Start with `delta = 15`.
    * For larger objects (for example ~50 mm object height), use `delta = 25` so motion updates quickly.
    * For small objects, tighten to `delta = 10`.
 * `alpha`: blending weight of current frame vs history. * Lower `alpha` (for example `0.1`) gives smoother output but can
   introduce lag/trails.
    * Higher `alpha` (for example `0.4`) updates faster but smooths less.
 * Important: `delta = 0` makes nearly every change significant and largely bypasses temporal blending.

##### Step 3: Speckle filter

Use `SpeckleFilterParams` to remove isolated blobs and salt-and-pepper-like artifacts in single frames.

 * `differenceThreshold`: threshold for grouping neighboring pixels into the same region.
 * `speckleRange`: maximum region size to remove.
 * Tuning strategy: gradually increase `speckleRange` to remove larger floating blobs, but avoid values that delete real small
   objects.

##### Step 4: Spatial filter

Use `SpatialFilterParams` to smooth object surfaces while preserving edges.

 * `alpha`: smoothing intensity (lower values usually smooth more).
 * `delta`: edge-preservation threshold; larger depth jumps are treated as edges and are not blended.
 * `holeFillingRadius`: fills invalid (`0`) pixels from surrounding valid data.
 * `numIterations`: more passes can refine output at slight processing cost.

##### Step 5: Median filter

Use `MedianFilterParams` for final aggressive cleanup when residual salt-and-pepper noise remains.

 * Supported modes in this node: `MEDIAN_OFF`, `KERNEL_3x3`, `KERNEL_5x5`.
 * Use the smallest kernel that solves the noise to minimize edge/detail loss.

#### Tuning quick reference

| Filter | Key Parameters | Primary Use Case | Trade-off |
| --- | --- | --- | --- |
| Temporal | `delta`, `alpha` | Smoothing dynamic noise over time | Smoothness vs motion lag |
| Speckle | `speckleRange`, `differenceThreshold` | Removing isolated blobs/noise | Cleanliness vs deleting small objects |
| Spatial | `delta`, `alpha`, `holeFillingRadius`, `numIterations` | Surface smoothing with edge preservation | Smoothness vs
processing cost |
| Median | `KERNEL_3x3`, `KERNEL_5x5` | Aggressive salt-and-pepper cleanup | Noise removal vs edge/detail blur |

## ToF motion blur

To reduce motion blur:

 * Increase sensor FPS (up to 160 FPS), while accounting for higher system load and reduced exposure time.
 * Disable phase shuffle temporal filter (higher noise).
 * Disable phase unwrapping (reduces max range).
 * Enable burst mode when applicable.

## Max distance

Maximum distance depends on modulation frequency and phase unwrapping settings.

```math
c = 299792458.0 # speed of light in m/s

MAX_80MHZ_M = c / (80000000 * 2) = 1.873 m
MAX_100MHZ_M = c / (100000000 * 2) = 1.498 m

MAX_DIST_80MHZ_M = (phaseUnwrappingLevel + 1) * 1.873 + (phaseUnwrapErrorThreshold / 2)
MAX_DIST_100MHZ_M = (phaseUnwrappingLevel + 1) * 1.498 + (phaseUnwrapErrorThreshold / 2)
```

#### RVC4

ToF node performs decoding and filtering on-device in the phase domain before depth is computed. No data is transferred to the
host for filtering. The outputs are:

 * `depth` (uint16, mm)
 * `amplitude` (uint8, log-compressed)
 * `intensity` (uint8, log-compressed)
 * `confidence` (uint8, log-compressed)
 * `raw` (packed, sensor-native format)

## Usage

#### Python

```python
pipeline = dai.Pipeline()

tof = pipeline.create(dai.node.ToF).build(
    boardSocket=dai.CameraBoardSocket.AUTO,
    profile=dai.ToFConfig.Profile.MID_RANGE,
)

depth_q      = tof.depth.createOutputQueue()
amplitude_q  = tof.amplitude.createOutputQueue()
intensity_q  = tof.intensity.createOutputQueue()
confidence_q = tof.confidence.createOutputQueue()
```

#### C++

```cpp
dai::Pipeline pipeline;

auto tof = pipeline.create<dai::node::ToF>()->build(
    dai::CameraBoardSocket::AUTO,
    dai::ToFConfig::Profile::MID_RANGE
);

auto depthQ      = tof->depth.createOutputQueue();
auto amplitudeQ  = tof->amplitude.createOutputQueue();
auto intensityQ  = tof->intensity.createOutputQueue();
auto confidenceQ = tof->confidence.createOutputQueue();
```

## Examples

 * [ToF Minimal](https://docs.luxonis.com/software-v3/depthai/examples/tof/tof_minimal.md) - minimal pipeline showing the `depth`
   output
 * [ToF All Queues](https://docs.luxonis.com/software-v3/depthai/examples/tof/tof_all_queues.md) - shows all main output queues on
   both RVC2 and RVC4
 * [ToF Align](https://docs.luxonis.com/software-v3/depthai/examples/tof/tof_align.md) - aligns ToF depth onto a mono camera
 * [ToF PointCloud](https://docs.luxonis.com/software-v3/depthai/examples/tof/tof_pointcloud.md) - colorized point cloud streamed
   to the DepthAI Visualizer
 * [ToF pointcloud + runtime tuning
   (oak-examples)](https://github.com/luxonis/oak-examples/tree/bbc446232ad9378e03e9cb89b04dbb9f99f2448f/depth-measurement/3d-measurement/tof-pointcloud)

## Reference

### dai::node::ToF

Kind: class

#### Subnode < ToFBase > tofBase

Kind: variable

#### Output & rawDepth

Kind: variable

Raw depth output from ToF sensor. On RVC2 this is connected to the unfiltered base depth output. On RVC4 this is an unconnected
placeholder output.

#### Output & depth

Kind: variable

Filtered depth output

#### Output & amplitude

Kind: variable

Amplitude output

#### Output & intensity

Kind: variable

Intensity output

#### Output & confidence

Kind: variable

Confidence output

#### Output & phase

Kind: variable

Phase output

#### Output & raw

Kind: variable

Raw data coming from the sensor

#### Input & inputConfig

Kind: variable

Runtime ToFConfig input for the ToF base node (decoder on RVC2, IPP on RVC4). RVC4: directly reconfigures the IPP that produces
ImageFilters . The host-filter stage that actually produces ImageFiltersConfig to

#### Input & tofBaseInputConfig

Kind: variable

Input config for ToF base node

#### Input * imageFiltersInputConfig

Kind: variable

Input config for image filters

#### ToFBase & tofBaseNode

Kind: variable

ToF base node

#### ImageFilters * imageFiltersNode

Kind: variable

Image filters node

#### ToF(const std::shared_ptr< Device > & device)

Kind: function

#### ~ToF()

Kind: function

#### void buildInternal()

Kind: function

Function called from within the

#### std::shared_ptr< ToF > build(dai::CameraBoardSocket boardSocket, dai::ImageFiltersPresetMode presetMode, std::optional< float > fps)

Kind: function

Build the ToF node with a specific board socket, legacy preset mode, and optional FPS. parameters: boardSocket: Board socket to
use, or AUTO to select an available ToF socket automatically; presetMode: Legacy ToF image filter preset mode; fps: Requested ToF
camera FPS

#### std::shared_ptr< ToF > build(dai::CameraBoardSocket boardSocket, dai::ToFConfig::Profile presetMode, std::optional< float > fps)

Kind: function

Build the ToF node with a specific board socket, profile, and optional FPS. parameters: boardSocket: Board socket to use, or AUTO
to select an available ToF socket automatically; presetMode: ToF processing profile to apply; fps: Requested ToF camera FPS

#### void postBuildStage()

Kind: function

### Need assistance?

Head over to [Discussion Forum](https://discuss.luxonis.com/) for technical support or any other questions you might have.
