3D visionHow-to

Measure distance and bend angle from depth point clouds in Python

Deproject depth pixels to 3D points in Python, compute straight-line distance and bend angle, and understand depth units, accuracy limits and calibration.

LumaSpec Vision8 min read Share on LinkedIn

On this page 12 sections
  1. 01What you need
  2. 02How deprojection works
  3. 03Step 1: get a frame and the intrinsics
  4. 04Step 2: read depth at your two pixels
  5. 05Step 3: deproject and compute the distance
  6. 06Step 4: compute the bend angle from three points
  7. 07Worked example
  8. 08How accurate is this, really?
  9. 09Automating the point picking
  10. 10Choosing the right sensor for the job
  11. 11Checklist before you trust the numbers
  12. 12Questions

You need two numbers from a 3D part: the straight-line distance between two features and the angle at which the part bends. A depth frame already contains that information. Deproject the pixel coordinates of each feature with the camera intrinsics, and the geometry falls out of simple vector math. This guide walks through the Python steps, the units to expect, a worked example with real arithmetic, and the accuracy limits that decide whether a depth camera is the right sensor at all.

If you have not yet chosen hardware, the 3D sensors category covers laser profilers, structured-light stereo cameras and ToF cameras; the camera finder filters them by type and range.

What you need

  • A depth camera streaming frames into Python through its vendor SDK, with the depth frame aligned to the colour or intensity frame if you pick points on a colour image.
  • The camera intrinsics (focal lengths and principal point) for the frame you sample, available from the SDK's profile or intrinsics call.
  • NumPy and a way to display or click on the image (OpenCV is the common choice).
  • A part with two or three features you can identify in the image.

A laser profile sensor is the precise end of this spectrum: it returns one profile per sweep rather than a full frame, which suits measuring along a line. For full-frame two-point work, a stereo camera or a projected structured-light camera gives you depth everywhere in the field at once.

How deprojection works

A pixel plus a depth value is a ray, not a point. The intrinsics turn the ray into a direction, and the depth turns it into a position:

X = (u - cx) × Z / fx Y = (v - cy) × Z / fy Z = depth(u, v)

Here (u, v) is the pixel, (cx, cy) the principal point, (fx, fy) the focal lengths in pixels, and Z the depth at that pixel. Most SDKs expose this as a single deprojection function that takes the intrinsics, the pixel and the depth, and returns the (X, Y, Z) point in metres. The center pixel depth measurement guide shows the same read-a-fixed-point pattern in detail.

Step 1: get a frame and the intrinsics

Start the camera, wait for a depth frame (and an aligned colour frame if you pick features visually), and read the intrinsics of the frame you will sample. Typical Python flow:

  1. Open the device and configure the depth stream resolution.
  2. Enable alignment so depth and colour pixels correspond, if you pick on colour.
  3. Fetch one frame set and pull the depth frame and its intrinsics.
  4. Convert the depth image to a NumPy array once; keep the frame object for per-pixel queries.

Step 2: read depth at your two pixels

The SDK's per-pixel distance call returns metres as a float. The NumPy array of the depth frame is usually in the sensor's native units (often millimetres as uint16), so pick one source of truth and convert explicitly. A single pixel is noisy; average a 5×5 patch around each point and reject zeros, which mark missing depth. The missing depth points guide explains why holes appear on dark, shiny or steeply angled surfaces.

Step 3: deproject and compute the distance

Call the deprojection function for each pixel, then take the Euclidean distance:

d = sqrt((X1 - X2)² + (Y1 - Y2)² + (Z1 - Z2)²)

In Python this is one NumPy line: d = np.linalg.norm(p1 - p2) with the two points as float arrays.

Step 4: compute the bend angle from three points

Take a point on each leg of the bend plus the vertex. Form two vectors from the vertex and use the dot product:

v1 = P1 - P3 v2 = P2 - P3 cos θ = (v1 · v2) / (|v1| × |v2|) θ = arccos((v1 · v2) / (|v1| × |v2|))

NumPy gives you np.arccos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2))) in radians; convert with np.degrees.

Worked example

Suppose deprojection returns, in metres:

  • P1 = (0.100, 0.050, 0.600)
  • P2 = (0.180, 0.050, 0.610)
  • P3 (the bend vertex) = (0.140, 0.050, 0.580)

Distance P1–P2: differences are (−0.080, 0, −0.010). Squared: 0.0064 + 0 + 0.0001 = 0.0065. Square root: about 0.0806 m, so 80.6 mm.

Bend angle: v1 = P1 − P3 = (−0.040, 0, 0.020), v2 = P2 − P3 = (0.040, 0, 0.030). Dot product: −0.0016 + 0 + 0.0006 = −0.0010. |v1| = sqrt(0.0016 + 0.0004) = 0.0447 m, |v2| = sqrt(0.0016 + 0.0009) = 0.0500 m. cos θ = −0.0010 / (0.0447 × 0.0500) = −0.447, so θ ≈ 116.6°. The interior bend angle is 180° − 116.6° ≈ 63.4°, depending on which convention your drawing uses. State the convention in your report.

Use the camera resolution calculator to check that your camera resolves the feature you are sampling: if the two points land a few pixels apart, deprojection error dominates the measurement.

Camera resolution for the smallest feature
Units
Pixels needed
–
Sensor size
– MP
Smallest sensor that fits
–
Pixel on the part
– mm
Pixels across the feature, that sensor
–

Pixels needed = field / feature × pixels per feature on each axis (field incl. margin). The lens must resolve the pixels too: Nyquist = 1000 / (2 × pixel µm) lp/mm.

Find cameras with enough pixels Open the full calculator

How accurate is this, really?

Three error sources stack up:

  1. Per-pixel depth noise. Structured-light and ToF cameras scatter by a few millimetres at close range, worsening roughly with distance squared. Averaging a patch and taking the median over several frames cuts this substantially.
  2. Pixel-selection error. If your clicked pixel is one feature edge off, the 3D point moves by Z / fx per pixel. At Z = 0.6 m and fx ≈ 500 pixels, one pixel is about 1.2 mm laterally.
  3. Calibration residuals. Factory intrinsics are good to a fraction of a pixel; that is rarely the limiting term. The depth scale is what to verify, with a gauge block at the working distance.

If you need better than about ±0.1 mm, this is the wrong tool: a laser profiler sweeping the bend line measures the angle from thousands of profile points instead of three. Which camera family fits which tolerance is the subject of the point cloud fusion and transform guides; if results must land in a robot or fixture frame afterwards, read transforming point clouds between frames before wiring the numbers into your process.

Automating the point picking

Clicking pixels works for setup and validation, not for production. Replace the clicks with feature detection: threshold the intensity image, find blobs or edges with OpenCV, take each feature's centroid, and sample the depth patch there. That also makes the measurement repeatable, because the same detector picks the same physical edges every cycle. For parts where the two features sit along one line, a dynamic per-part ROI keeps bandwidth and processing down.

Choosing the right sensor for the job

Need Sensor type Why
Full-frame two-point distance, ±1–3 mm Structured light or ToF Depth everywhere in one frame; simple picking
Bend angle along a line, ±0.1 mm Laser profiler Thousands of points per profile; angle fitted, not guessed
Large parts, coarse volume Logistics 3D camera Wide field, box-level accuracy
Long range outdoor ToF Less sensitive to ambient light than stereo

Browse the 3D sensor finder by type and working distance, or start at the camera step of the configurator and add the mount, lighting and controller around it.

Checklist before you trust the numbers

  • Confirm the units of the distance call and the depth array; convert once, explicitly.
  • Average a 5×5 (or larger) patch at each point; reject zero-depth pixels.
  • Take the median over 5–10 frames to suppress temporal noise.
  • Verify against a gauge block or a part with a certified dimension at the real working distance.
  • Record which angle convention (interior or exterior) your code reports.
  • Log the intrinsics and firmware version with each measurement for traceability.

If a measured distance jumps by roughly 1000× or the part appears cut off near the camera, the depth scale or units are wrong; the depth scale mismatch guide covers that failure in depth. For everything else that goes wrong with point clouds, the 3D topic hub collects the guides in one place.

When you know the sensor type but not the model, tell our applications team the working distance, target tolerance and part material through the contact page — we will shortlist two or three candidates and the supporting lens and lighting, with free expert support on every order.

Questions

What units does a depth frame return in Python?

Most depth camera SDKs return distance in metres as a float, and deprojection functions expect metres too, so the resulting 3D point coordinates are in metres as well. Multiply by 1000 for millimetres. Always confirm the unit in your SDK's API reference before publishing a measurement, because a factor-of-1000 error is easy to miss on a bench part.

How accurate is a two-point distance measurement from a depth camera?

Expect single-point depth noise in the low millimetres at close range for a structured-light or ToF camera, growing roughly with the square of distance. A distance between two points combines the noise of both, so a practical tolerance is a few millimetres for parts a few hundred millimetres away. For sub-millimetre work, use a laser profiler instead.

How do I pick the right pixels automatically instead of clicking?

Run a blob or edge detector on the intensity or depth image, take the centroid of each feature of interest, then read the depth at those centroids. Average a small neighbourhood around each centroid rather than a single pixel to reduce noise. If features sit on a moving line, a laser profiler captures the profile in one sweep.

Do I need to calibrate before measuring distances this way?

The factory intrinsics and extrinsics are adequate for most dimensional checks; deprojection uses them directly. What matters is that the depth scale is correct and the target surface returns clean depth. For measurements that must relate to a robot or fixture frame, run an extrinsic hand-eye or fixture calibration on top of the factory data.

Why does my computed angle change between frames?

Depth noise at the vertex point rotates both vectors, and the error grows when the two legs are short or nearly parallel to the viewing axis. Average depth over a patch at each of the three points, capture several frames and take the median, and keep the bend line roughly facing the camera.