World and imaging
How the virtual camera turns a 3D scene into an image. Open model, closed implementation.
The engine is closed. The model is not. Why should you trust numbers a virtual device produces? Because the model is written out in full and you can check the arithmetic. That is the only reason this chapter exists.
Scene representation
The stage is described analytically, not as a triangle mesh:
- Planes: calibration targets, part surfaces, carriers — each with a pose
T_W_Objand extents; - Patterns: circle grids, checkerboards, fiducials, scribed lines — defined in the plane's local 2D coordinates;
- Materials: reflectance and roughness, affecting grey level and contrast only (no global illumination).
The reason for doing it this way: ground truth is available analytically. The world coordinate of a circle centre is an exact value, not something recovered from a render. Truth scoring works only because of this.
Pinhole model with distortion
From a world point p_W to a pixel (u, v):
p_C = T_C_W · p_W rigid transform into camera frame
x = p_C.x / p_C.z, y = p_C.y / p_C.z normalised plane
r² = x² + y²
x' = x(1 + k₁r² + k₂r⁴ + k₃r⁶) + 2p₁xy + p₂(r² + 2x²) radial + tangential
y' = y(1 + k₁r² + k₂r⁴ + k₃r⁶) + p₁(r² + 2y²) + 2p₂xy
u = fx·x' + cx
v = fy·y' + cy
Parameters and typical magnitudes:
| Parameter | Meaning | Value on demo-2axis-vision |
|---|---|---|
| fx, fy | Focal length in pixels | 7246.4, 7246.4 (25 mm lens / 3.45 µm pixels) |
| cx, cy | Principal point | 1224.0, 1024.0 (slightly off geometric centre — normal) |
| k₁ | First radial term | −0.0214 (barrel) |
| k₂ | Second radial term | 0.0031 |
| p₁, p₂ | Tangential terms | 1.2e−4, −0.8e−4 (sensor not perfectly parallel to lens) |
fx ≠ fy means non-square pixels, which industrial cameras rarely have. If your calibration returns two values differing by more than 1 %, suspect the target dimensions or the data before accepting it.
From continuous to discrete
An image is not "sample the scene at the circle centre". It is an integral over the pixel area:
I(u,v) = ∫∫_pixel L(x,y) · PSF(x,y) dx dy + noise
PSF comes from Optics and defocus, noise from
Noise. We approximate the integral with area-weighted supersampling
(4×4 by default).
That leads to a fact worth knowing: subpixel circle extraction works because edge intensity is
a gradient, and the gradient is the result of that integral. Turn supersampling off
(supersample: 1) and centre extraction immediately degrades to ±0.5 px — which is also what
happens on a real camera when overexposure kills the edge.
Grey level and exposure
DN = clamp( g · (Φ · t_exp · QE / e_full) · (2^bits − 1) + offset, 0, 2^bits − 1 )
| Symbol | Meaning |
|---|---|
| Φ | Photon flux at the pixel (reflectance × illumination) |
| t_exp | Exposure time |
| QE | Quantum efficiency |
| e_full | Full well capacity |
| g | Gain (dB converted to linear) |
Overexposure clips hard, it is not soft-compressed. That is deliberate: real sensors clip, and clipping-induced circle-centre bias is a very common source of calibration error. The model has to be able to reproduce it.
What this model does not do
Stated plainly so you do not assume otherwise:
- No global illumination — no interreflections, no cast shadows;
- No polarisation or interference;
- No chromatic aberration (monochrome imaging, as most machine vision is);
- No row/column fixed-pattern noise (per-pixel noise and bad pixels only).
If you need to validate any of the above, the virtual device cannot answer it and you need real hardware.