ELAI S.r.l.

The camera sees the part: how far away is it really?

The camera sees the part: how far away is it really? A worked example explains assumptions and limits.

The camera sees the part: how far away is it really?

Recognising a part does not reveal its distance

A camera locates a component’s image centre, but robotic grasping needs spatial coordinates. How much distance information is in that pixel? We derive monocular ambiguity, show what two calibrated cameras add, and calculate how one pixel of error affects depth. Results are executed ideal geometry, not robot measurements.

A pixel identifies a ray, not a unique point

Use an ideal pinhole camera with X, Z in metres, positive Z along the optical axis, focal length f in pixels, and u relative to the principal point. Projection is u=fX/Z. Multiplying X, Z by the same positive factor preserves u, so the ratio is known but not metric scale. At f=800, (X, Z)=(0.2,2) and (0.4,4) both give u=80 pixels.

u = f X/Z; f (aX)/(aZ) = f X/Z, a>0

Monocular systems can still use known planes, sizes, calibrated motion or learned regularities. A depth model adds training-based assumptions; it does not remove single-projection ambiguity without them. If size is unknown, interpreting “looks small” as “far away” is a prior choice, not guaranteed geometry.

Two cameras make a difference observable

Put a second camera B metres along X, parallel axes and equal focal length. Images are rectified, coordinates centred, and the same point correctly matched. Then u_L=fX/Z and u_R=f(X−B)/Z. Subtracting eliminates X: disparity d=u_L−u_R=fB/Z. Known baseline B supplies the missing metric length.

d = u_L−u_R = fB/Z; Z = fB/d, d>0

With f=800 pixels and B=0.1 m, fB=80 pixel-metres. Depths two and ten metres give disparities forty and eight pixels. For X=0.2, Z=2, u_L=80, u_R=40 and Z=80/40=2 m. Pixel units cancel correctly. Zero, negative or invalid disparity requires checking conventions and matching, not treating it as ordinary positive depth.

One pixel matters far more at long range

Holding f, B fixed gives dZ/dd=−fB/d² and local δZ≈−fBδd/d². More disparity means less depth; the magnitude measures sensitivity. It is 0.05 m per pixel at two metres, 1.25 at ten. These are local slopes, not standard deviations or confidence intervals.

Without linearisation, d=40±1 gives depths 80/41≈1.951219 to 80/39≈2.051282 m. At 8±1, bounds are 80/9≈8.888889 and 80/7≈11.428571 m. Deviations are asymmetric because inversion changes uncertainty shape. If disparity reaches zero, no finite upper depth bound remains in this model.

Z (m)d (px)Z(d+1) (m)Z(d−1) (m)
2.040.01.951222.051282
10.08.08.88888911.428571
Depth grows rapidly near zero disparity. Bars represent ±1 pixel, not confidence intervals. Ideal geometry, no camera measurement.
Depth grows rapidly near zero disparity. Bars represent ±1 pixel, not confidence intervals. Ideal geometry, no camera measurement.

Geometry does not replace calibration and real testing

OpenCV documents stereo geometry and matching limits including low texture, edges and noise. We did not execute StereoBM or compare depth networks. The formula assumes correct correspondence; a mismatch can yield a finite but meaningless number. Focal, baseline, distortion and timing errors are outside the ±1-pixel bars, which perturb only d while holding f, B exact.

Industrial and collaborative robotics is an EL-AI exploration direction, not an installation demonstrated here. Grasp commands need camera, camera-robot frame, uncertainty and task validation. This derivation certifies neither safety, precision nor manipulation success. Cheap scalar inversion also says nothing about whole-image correspondence cost.

A single pixel gives direction; calibrated stereo can give distance; physical validation determines task adequacy. Greater range increases disparity sensitivity. Recognising the part is useful but insufficient to position the gripper.

OpenCV 4.13.0 — Stereo depth geometry.

import json,sys
from pathlib import Path
f=800.;B=.1
rows=[]
for Z in [2.,10.]:
 d=f*B/Z;rows.append(dict(Zm=Z,disparityPx=d,lowZm=f*B/(d+1),highZm=f*B/(d-1),localSensitivityMPerPx=f*B/d**2))
assert f*.2/2==f*.4/4==80
assert rows[0]['disparityPx']==40 and rows[1]['disparityPx']==8
r=dict(python=sys.version.split()[0],seed=None,scope='Ideal calibrated rectified pinhole geometry; no real camera or robot measurement',focalPx=f,baselineM=B,rows=rows,disparityGrid=list(range(2,81)),depthGrid=[f*B/d for d in range(2,81)],scaleAmbiguity=[[.2,2.,80.],[.4,4.,80.]])
Path(__file__).with_name('results.json').write_text(json.dumps(r,indent=2),encoding='utf8')

Code, data, and instructions · JSON. Educational calculations executed with Python 3.14.0; figures with Matplotlib 3.11.2. AI-assisted analysis, without claiming peer review or human review. Original illustrative ImageGen cover: it does not document EL-AI people, premises, or installations. Sources accessed 25 October 2026.