Noise
Shot noise, read noise, dark current, bad pixels — and how each one limits calibration accuracy.
Three terms
Every pixel of the virtual camera gets:
DN = DN_ideal + n_shot + n_read + n_dark
n_shot ~ Poisson(e_signal) signal dependent, larger in bright areas
n_read ~ N(0, σ_read²) signal independent, dominant in the dark
n_dark ~ Poisson(i_dark · t_exp) proportional to exposure time
Defaults, modelled on a common industrial CMOS:
| Parameter | Value | Note |
|---|---|---|
| σ_read | 2.3 e⁻ | Read noise |
| i_dark | 12 e⁻/s | Dark current at room temperature |
| e_full | 10500 e⁻ | Full well |
| QE | 0.62 @ 530 nm | Quantum efficiency |
| bits | 8 or 12 | Selectable |
Signal-to-noise ratio
SNR = e_signal / sqrt(e_signal + σ_read² + i_dark·t_exp)
Once the signal is strong, SNR ≈ sqrt(e_signal) — double the brightness and SNR improves by
only 1.41×. That answers a question we get often: why cranking exposure barely helped.
How noise limits calibration
This is the part that matters.
Circle centre extraction
A centre is derived from subpixel edge positions, each jittering by σ_edge. For a circle with
N edge points:
σ_centre ≈ σ_edge / sqrt(N)
So bigger circles give steadier centres. This is why calibration target circles should not be small: a 10 px circle and a 30 px circle differ by about 1.7× in centre precision.
Pixel size
Pixel size is solved from distances between centres, and least squares averages some of the centre error away:
σ_scale / scale ≈ σ_centre / (D · sqrt(M))
where D is the acquisition span in pixels and M the number of poses.
Two practical conclusions:
- Span matters more than pose count. Nine poses across 50 mm beat fifty poses across 5 mm, by a lot.
- Going from 9 to 36 poses improves precision 2×; going from 5 mm to 50 mm of span improves it 10×.
Beginners almost always do the opposite — a dense cluster in the centre of the field, followed by surprise that it is not accurate.
Focus
See Optics and defocus.
Bad pixels
Generated at a configured rate (0.001 % by default), in two kinds:
- hot: stuck at a high value;
- dead: stuck at zero.
Positions are fixed for the lifetime of a virtual machine, as on a real sensor. That makes bad pixel compensation genuinely testable: get the map right and changing exposure will not make them reappear.
Configuration
Devices → Virtual devices → Camera → Noise:
"noise": {
"enabled": true,
"readNoiseE": 2.3,
"darkCurrentEPerS": 12,
"fullWellE": 10500,
"qe": 0.62,
"badPixelRatio": 1e-5,
"seed": 20260921
}With seed fixed, noise is reproducible — the same input gives the same image. That is what
makes an assertion like "centre error < 0.05 px" meaningful in an automated test. Set seed to
null for fresh randomness each run, which feels more like real hardware and suits teaching.