Added noise
This commit is contained in:
1 parent
970551b4e1
commit
1ed95982ad
14 files changed
+687
-22
No files matched your search
+97
-4
@@ -356,7 +356,8 @@ For the example camera, pi * A * t / (4 N^2) is 1.48e-16 m^2 sr s.
|
||||
|
||||
## From electrons to DN: the detector
|
||||
|
||||
Code: `camera.rs` (`Detector::dn`); `gpu/sensor.rs`, `shaders/sensor.wgsl`.
|
||||
Code: `camera.rs` (`Detector::dn`, `Detector::variance`); `gpu/sensor.rs`,
|
||||
`shaders/sensor.wgsl`.
|
||||
|
||||
- **Full well.** The maximum number of electrons a pixel holds. Beyond it
|
||||
the pixel saturates and reads its maximum value whatever the light.
|
||||
@@ -371,7 +372,8 @@ Code: `camera.rs` (`Detector::dn`); `gpu/sensor.rs`, `shaders/sensor.wgsl`.
|
||||
So DN = bias + min(electrons, full well) / gain, rounded and clipped to the
|
||||
ADC range. The renderer does this on the GPU for every pixel and channel,
|
||||
as the last step of a frame, and reads back only the resulting integers.
|
||||
Noise, not modeled yet, enters between the electrons and the gain.
|
||||
From camera tier C1, noise enters between the electrons and the gain (see
|
||||
Noise below).
|
||||
|
||||
Two consequences of these numbers show up in every image:
|
||||
|
||||
@@ -411,7 +413,98 @@ exactly, on the GPU and on lavapipe.
|
||||
|
||||
Picking the exposure time is the same trade a photographer makes. A longer
|
||||
exposure saturates the sunlit target. A shorter one leaves the side of the
|
||||
target lit only by Earthshine lost in noise once noise is modeled.
|
||||
target lit only by Earthshine lost in noise.
|
||||
|
||||
### Noise
|
||||
|
||||
Code: `Detector::variance`; `sensor.wgsl` (`NOISE`).
|
||||
|
||||
A real image is noisy, and the noise decides what can be detected at all.
|
||||
From camera tier C1 the renderer adds three kinds, following the EMVA 1288
|
||||
standard's model of a camera:
|
||||
|
||||
- **Shot noise.** Light arrives as photons at random times, so a pixel
|
||||
expecting N electrons collects a Poisson-distributed count with mean N and
|
||||
standard deviation sqrt(N). It is a property of light, not of the camera:
|
||||
a perfect detector still has it. A pixel expecting 10,000 electrons varies
|
||||
by 100 (1 %); one expecting 100 varies by 10 (10 %).
|
||||
- **Dark current.** The detector makes electrons of its own from heat,
|
||||
`Detector::dark_current` per second, which add to the light's and carry
|
||||
shot noise of their own. It grows with temperature, roughly doubling every
|
||||
6 to 8 degrees C; the value given is for the detector's operating
|
||||
temperature.
|
||||
- **Read noise.** Reading a pixel out adds a few electrons of random error,
|
||||
`Detector::read_noise` RMS, to every pixel in every frame, light or not.
|
||||
|
||||
In order: the light's electrons and the dark electrons together are drawn
|
||||
as one Poisson count (a sum of Poisson counts is itself one); the count is
|
||||
clipped to the full well; read noise is added; then the gain, bias and
|
||||
rounding to DN as before. Below a mean of 50 electrons the count is drawn
|
||||
exactly. Above it a Gaussian of the same mean and variance takes its place,
|
||||
which a Poisson count's shape is already close to and which costs the same
|
||||
at any level. With read noise the DN can fall below the bias, which is what
|
||||
the bias is for.
|
||||
|
||||
Each pixel's variance in electrons^2 is then
|
||||
|
||||
variance = (electrons + dark current x exposure) + read noise^2 + gain^2 / 12
|
||||
|
||||
where the last term is the rounding to whole DN. Divided by gain^2 it is in
|
||||
DN^2. Plotted against the mean signal in DN, the variance is a straight line
|
||||
with slope 1 / gain and intercept (read noise^2 + dark shot noise) / gain^2:
|
||||
the photon transfer curve, the standard way to measure a camera's gain and
|
||||
read noise from flat fields alone.
|
||||
|
||||
For the example camera (5 electrons read noise, 20 electrons per second dark
|
||||
current, gain 5) at 0.1 ms, the signal-to-noise ratio, signal over the
|
||||
square root of the variance, is:
|
||||
|
||||
| What | Electrons | SNR |
|
||||
|---|---|---|
|
||||
| A white surface facing the Sun, green channel | 11,139 | 105 |
|
||||
| Earth, albedo 0.3 | 3,342 | 58 |
|
||||
| A white surface lit by 1 % of sunlight, as Earthshine can | 111 | 9.5 |
|
||||
| A V = 0 star, over the 4 pixels it covers | 56 | 4.4 |
|
||||
| A V = 4 star, over 4 pixels | 1.4 | 0.13 |
|
||||
|
||||
So at the exposures a sunlit target needs, the brightest stars are just
|
||||
detectable and the rest are lost in read noise. At 50 ms, a star tracker's
|
||||
exposure, a V = 6 star reaches an SNR of 7.5.
|
||||
|
||||
Dark current hardly matters at these exposures: 20 electrons per second is
|
||||
0.002 electrons in 0.1 ms. It matters for long ones: 1 electron in 50 ms,
|
||||
100 in 5 s, with 10 electrons of shot noise.
|
||||
|
||||
**Reproducible randomness.** The noise comes from a counter-based generator:
|
||||
a hash (Jarzynski and Olano's pcg4d) of the pixel and channel, the frame
|
||||
number and a 64-bit seed (`RendererOptions::noise_seed`), turned into
|
||||
uniform numbers, then into Gaussians (Box-Muller) or a Poisson count (by
|
||||
inverting its distribution). Nothing is carried from one pixel or frame to
|
||||
the next, so every value is computed independently on the GPU. The renderer
|
||||
numbers its frames from 0 (`Frame::number`), and
|
||||
`Renderer::set_frame_number` renders a frame again with exactly the same
|
||||
noise. Different frame numbers or seeds give independent noise. The hash is
|
||||
identical on every device, but the logarithms and cosines that shape it may
|
||||
differ in the last bit, so the GPU and lavapipe agree statistically rather
|
||||
than pixel for pixel.
|
||||
|
||||
**Noise-free images.** A detector with no read noise, no dark current and
|
||||
shot noise off (`Detector::noiseless`) renders C1 without noise, exactly as
|
||||
before noise existed, for ground truth and ML labels. The renderer then
|
||||
compiles the noise out entirely. At C0 there is never noise.
|
||||
|
||||
Measured, identically on the GPU and lavapipe: flat fields at six levels
|
||||
from 100 to 16,000 electrons, two frames each, give half the variance of
|
||||
their difference within 2 % of the formula (0.982 to 1.008 of it, against a
|
||||
sampling error of 0.6 %), and the photon transfer curve's slope is 1 / gain
|
||||
to 0.1 %. A dark frame of 100 electrons reads 20.005 DN above the bias, with
|
||||
its variance within 0.6 %. Counts below 50 match the Poisson probabilities
|
||||
count by count. Consecutive frames, different seeds and neighboring pixels
|
||||
are uncorrelated (|r| at most 0.005 over 196,608 values). 400 faint stars of
|
||||
300 electrons each vary as predicted to 0.4 %, an SNR of 7.35. Noise costs nothing measurable on an
|
||||
RTX 3060 (0.476 ms for the sensor pass at 1920 x 1080 with or without it,
|
||||
1.89 ms at 3840 x 2160); on lavapipe it adds 1.5 ms at 1920 x 1080 and
|
||||
9.6 ms at 3840 x 2160.
|
||||
|
||||
## How shapes become pixels
|
||||
|
||||
@@ -1461,7 +1554,7 @@ is a run of the script with `--gaia 16`, at twice the size.
|
||||
|
||||
- Earthshine on the Moon's night side
|
||||
- The diffraction pattern's rings, and a lens's measured point spread function
|
||||
- Noise: photon shot noise, read noise, dark current, pixel-to-pixel variation
|
||||
- Pixel-to-pixel variation in response and dark current (PRNU, DSNU)
|
||||
- Saturation spilling into neighbors (blooming)
|
||||
- Non-Lambertian materials (BRDFs), glints, and the full Moon's edge as bright
|
||||
as its center
|
||||
|
||||
Reference in new issue
Block a user