A raw depth frame holds unsigned 16-bit integers — 3250, 4096, 0 — where you expected meters. Every SDK for stereo, structured-light and time-of-flight depth cameras ships the fix as one number: the depth scale, in meters per raw unit. The confusion is that two code snippets circulate, one multiplying each pixel by the scale and one dividing. Both are correct, because they belong to different frame formats. This page gives the multiplication rule, where the scale is read from, a worked example at the usual 1 mm unit, the disparity exception, and the checks that catch a wrong conversion before it reaches your robot or PLC.
The rule: one multiplier from the depth sensor
The depth sensor carries a single float property: how many meters one raw unit represents. Call it the depth scale. For any depth-format frame the whole conversion is:
Frame data is an array of 16-bit unsigned integers, which is why the standard snippet casts the frame pointer before indexing it. On the common factory setting one raw unit is 1 mm, so the scale reads 0.001 and the arithmetic is the schoolbook rule, 1000 mm = 1 m. Zero is reserved: a raw value of 0 marks a pixel with no measurement, so mask it or flag it, but never read it as a distance.
The step is identical in kind to turning raw analog counts into engineering units on a PLC; the PLC analog scaling tutorial: engineering units versus raw values works the same arithmetic for input modules.
Why a divide version exists: DISPARITY16 frames
Disparity is not depth. A stereo depth camera measures the horizontal offset, in pixels, between the same image feature seen by its two sensors. Offset and distance are inversely proportional — a near object has a large disparity, a far object a small one — which gives the stereo relation and the disparity-frame conversion:
A camera that transmits disparity instead of distance packs it into 16-bit values under the format name DISPARITY16, with its scale arranged so the division lands in meters. Most stereo depth cameras on the market never transmit DISPARITY16; their depth output is distance already, so multiplication is the only conversion you will run. The dividing snippet describes a frame format, not a second family of hardware. Feeding depth values into it is not a small error: 0.001 ÷ 3250 gives 3.1×10⁻⁷ m, about a third of a micrometer, for a wall that sits at 3.25 m.
How a disparity scale is assembled from baseline and focal length, and why it needs calibration, is covered step by step in Stereo disparity scale and calibration for depth estimates.
Worked example: one pixel and one frame at the usual 1 mm unit
The case: a 1280 × 720 depth stream looking across a workshop bay, one pixel aimed at the far wall, scale queried when the stream opens. All readings below are example values.
- Depth scale from the depth sensor: 0.001, meters per raw unit (the 1 mm setting)
- Raw value at the wall pixel: 3250, 16-bit unsigned
- Frame size: 1280 × 720 = 921,600 pixels
- Invalid pixels: raw value 0
The rule in words: read the scale once from the depth sensor, then multiply every raw value by it.
depth_scale = read_depth_scale(depth_sensor) # float, meters per raw unit, queried once per stream
for raw_value in depth_frame: # uint16, 0 = no measurement
depth_m = depth_scale * raw_value
The arithmetic, step by step:
- One pixel: 3250 × 0.001 = 3.25, so the wall sits 3.25 m from the sensor. Cross-check through millimeters: 1 mm per unit gives 3250 units = 3250 mm, and 3250 mm ÷ 1000 = 3.25 m. The two paths agree, so scale and units are consistent.
- Whole frame: 1280 × 720 = 921,600 pixels. One scale query, then one array multiply. A per-pixel helper call enters the camera driver 921,600 times per frame; caching the scalar replaces all of them with a single pass.
- Invalid pixels: 0 × 0.001 = 0.0 m. That zero flags missing data, not an object at the lens, so mask raw zeros before any minimum, maximum or average.
- Wrong-path check: 0.001 ÷ 3250 = 3.1×10⁻⁷ m. Sub-millimeter output for a wall is the signature of the disparity formula applied to a depth frame.
The decision: converted distances must line up with a tape measure. A wall a few steps away reads about 3.25 — not 3250 (scale never applied), not 0.00325 (applied twice: 3.25 × 0.001 = 0.00325 m), and not 3.1×10⁻⁷ (divided instead of multiplied).
Where the scale comes from, and when it changes
Query the value from the depth sensor itself, through the SDK property that reports the unit size in meters per raw unit, and not from the color or infrared module. On the common 1 mm setting it returns 0.001.
Read it after the stream is configured, then again after any settings change: resolution, unit mode and emitter settings can all move it. Streaming SDKs cache the scale in exactly this way in their alignment examples, because per-pixel queries across the library boundary dominate frame time otherwise.
Checks that catch a wrong conversion
Four checks, two minutes, and every failure mode above shows up:
- Known target: tape a flat board at a measured distance — 1.000 m is convenient — and read the converted depth at its center. It should land within the depth-error figure the camera's data sheet states for that distance.
- Range scan: convert the frame and print minimum, maximum and mean with zeros masked. The maximum should match the farthest visible object; a raw maximum pinned at 65535, the 16-bit ceiling, is saturation or fill, not a 65.5 m wall.
- Axis sense: a pixel's Z is distance along the camera's Z axis, not the straight-line range for off-center pixels. The off-axis correction is laid out in Depth value vs true distance: convert a pixel's Z to camera distance.
- Datatype check: float arrays that read in the 1-to-10 range for a room-scale scene are already meters; a second 0.001 multiply shrinks a 3.25 m wall to 3.25 mm. Unsigned 16-bit arrays are raw units — find the scale before multiplying.
Converted depth still carries the sensor's noise, and smoothing it into measurements worth tolerancing is its own step: Stereo depth map noise: smooth raw depth into usable data shows the workflow.
When there is nothing to convert
Two cases skip the scale. Laser profile sensors and calibrated 2.5D systems are calibrated to physical units at the factory, so profile and height data arrives in millimeters, ready for tolerance checks. Point clouds the SDK builds itself are already in meters; the remaining question there is per-axis scale correction, covered in Depth image to point cloud: apply per-axis scale factors.
Classify the data before writing conversion code: unsigned 16-bit integers mean raw units and a scale to find; floats near the 1-to-10 range for a room scene mean meters already; profile arrays mean millimeters already. One glance at type and magnitude settles which snippet applies.
Choosing sensors and the next step
The rule covers every depth-frame camera: stereo, projected structured light and time of flight, with profile sensors as the calibrated exception. The 3D sensors category gathers the families, and the camera finder filters them by type and series.
A representative from each depth-frame family:
- Type
- Laser profiler
- Type
- Stereo / structured light
- Type
- Depth / ToF camera
For mounting height, tilt and depth range around your part, pair this page with the 3D camera working distance and positioning guide, then assemble the rest of the station in the configurator:
The configurator matches a camera, lens, light, light controller and cables to each other, step by step, and lists only parts that fit what you already picked.
Related guides sit under the 3D camera topic page. If a converted frame still disagrees with your tape measure, contact the team with the scale your SDK reports and one raw frame, and we will walk the arithmetic with you.
Questions
Where do I read the depth scale from?
It is a property of the depth sensor, exposed by your SDK as a unit query and returned as a float in meters per raw unit. Query the depth sensor specifically, not the color or infrared module, and do it after the stream is configured. If a resolution, unit or emitter setting changes, the scale can change with it, so log the value with your configuration and re-query after any change.
Why do some code examples divide by the pixel value instead of multiplying?
Those snippets target a DISPARITY16 frame. Disparity is the stereo match offset in pixels and inversely proportional to distance, so depth equals the frame's scale divided by the raw value. Most stereo depth cameras do not offer that output and deliver plain depth, where multiplication is always correct. Division on a depth frame returns values near 0.0000003 m where you expect 3.25 m.
Does a raw value of 0 mean the object is right at the camera?
No. In a 16-bit depth frame a raw zero marks a pixel with no measurement: too dark, too shiny, out of range or occluded. The multiplication turns it into 0.0 m, a quality flag rather than a position. Mask raw zeros before computing minima, maxima or averages, and treat a sudden rise in the zero-pixel count as an exposure or reflectivity problem, not objects moving closer.
Do I need a depth scale for a laser profiler?
No. Laser profile sensors and calibrated 2.5D systems are factory-calibrated to physical units, so their profiles arrive in millimeters and need no per-pixel multiplier. The depth-scale rule is for depth-frame cameras: stereo, structured light and time of flight. Watch float point clouds too: values that already read in the 1-to-10 range for a room-scale scene are meters, and a second 0.001 multiply would shrink a 3.25 m wall to 3.25 mm.
Can the depth scale change after start-up?
Yes. The scale belongs to the configured stream: switching resolution, unit mode or emitter settings can change it. Query it once when the stream opens, log it beside your configuration, and re-query after any settings change or firmware update. If every distance suddenly moves by a factor of 1000, print the scale before debugging geometry, because the unit changed, not the scene.


