Nodes/ComfyUI CV/cv2.rescaleDepth
ComfyUI Node

cv2.rescaleDepth

Millimetres to metres, and why your depth PNG lies

By bmad4ever·Created 4 months ago·Updated 14 days ago· 1
cv2.rescaleDepth
  • in_
  • nparray
◄typeCV_32F►
◄depth_factor1000.0000►

The unit problem, in one node

Raw depth from a Kinect-style sensor is a 16-bit integer in millimetres. Raw depth from a stereo rig might be float metres. Raw depth from a depth-estimation model is relative nonsense with a pretty gradient. Meanwhile the node that wants to consume it - a point-cloud builder, an occlusion mask, a reprojection - wants metres as floats, and it will not tell you if you got that wrong. You just get geometry that's 1000× too big, or a cloud that's suspiciously smooth.

cv2.rescaleDepth does the conversion. It's small, it's the node that saves you from debugging a scale factor by hand across five nodes, and its behaviour is entirely documented by the pack's own tooltips.

Inputs and what they do

  • in_ - the depth map. The author's tooltip: "Depth map. CV_16U is read as millimetres and divided by depth_factor; any other type is just converted to float." So the interesting branch is the 16-bit-unsigned one.
  • type - output depth, CV_32F (the default) or CV_64F. If you're feeding a point cloud, CV_32F is plenty; doubles only help if you're accumulating a lot of them.
  • depth_factor (optional, default 1000) - "Divisor turning raw integer depth into metres (cv2 default 1000.0 = millimetre depth, the Kinect convention). Only used for a CV_16U input; zeros become NaN."

That last clause is the important one and it's worth reading twice: zeros become NaN. In a depth map, 0 means "no reading" (object too close, too far, or a reflective surface), and the node converts that into NaN rather than dragging a meaningless 0 m into your geometry - a 0 that would otherwise become a point sitting exactly at the camera, which is a spectacular way to ruin a bounding box. The flip side: NaNs propagate. Guard them downstream with the pack's cv2.finiteMask (255 where every channel is finite) or clean them with cv2.patchNaNs, and check how your viewer reacts to NaN before you export a PLY.

Output: nparray - a float depth map.

The trap that catches everyone

A 16-bit depth PNG loaded through Image Load Image is not a 16-bit depth map any more. ComfyUI's IMAGE is a float tensor in 0–1 derived from 8 bits, and the pack converts an IMAGE to uint8 before handing it to cv2. So your carefully stored millimetre values arrive as 0–255, and rescaleDepth cheerfully takes the "any other type is just converted to float" branch - meaning your depth is now linear, plausible-looking, and off by a factor of 257. Nothing errors. The cloud renders. It's just wrong.

If you have genuine 16-bit depth, keep it as an NPARRAY through that part of the graph (the pack is full of explicit IMAGE↔NPARRAY/CV bridged nodes - CV Image To CV and friends) and only convert to an image when you want to look at it, normalised. Metric data and display data are different things, and this is where they get confused.

The second trap, cheaper to fix: a depth map that's already in metres getting divided by 1000 again. Set depth_factor to 1 for pre-converted data, or skip the node entirely.

Where it sits in a pipeline

Upstream of anything geometric: cv2.rescaleDepth → filter/clean → back-project to a cloud (CV Depth to 3D Points), or feed a registered depth map into the RGB-D alignment path (cv2.registerDepth), or use it to build an occlusion mask against a rendered object (the pack's AR occlusion example compares a scene's depth against a rendered depth - same units required, or the comparison is meaningless). If you're doing stereo from a rectified pair rather than a depth sensor you probably don't need this node at all; disparity and cv2.reprojectImageTo3D handle their own units through Q.

Install

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Manager: search "ComfyUI CV" by bmad4ever. Requirements: Python ≥ 3.12 and a ComfyUI built on the V3 node API. Restart ComfyUI and reload the browser page when it's installed.

Troubleshooting

Output is all NaN. Every input value was zero - often because the depth image is empty, or because it arrived as an all-black 8-bit frame and everything under one millimetre rounded to nothing.

Geometry is 1000× too large. You skimmed the tooltip: the divisor only applies to 16-bit input, so either your data was already metric (pass depth_factor = 1) or it arrived as uint8 and the conversion silently no-opped.

Zeros where you expected holes. You're looking at the input, not the output - remember the node turned those into NaN, which some previews render as black and some as white.

Node absent from the menu. The pack generates its registry from the installed OpenCV build and skips what that build lacks. Check yours:

python -c "import cv2; print(cv2.rescaleDepth)"
Categoryimage/CV/low-level/cv2 R

Inputs (3)

NameTypeDefaultDescription
in_NPARRAY,IMAGE,MASK - - - Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
typeCOMBOCV_32F - - -
depth_factoroptFLOAT1000.0000-1e+38–1e+38 - - - Preset to the OpenCV default (1000.0).

Outputs (1)

NameTypeDescription
nparrayNPARRAY—