Files
DarkRoom/docs/dev/sensor-health.md
dtourolle 8f9e59b9fa Find hot photosites without repairing them, and measure a sensor's aging
The hot-pixel pass could only repair: it returned how many photosites it
changed and threw away which. find_hot_pixels runs the same pass and
returns them as sensor coordinates, leaving the frame alone, so a sensor's
defects can be tracked across frames.

sensor_scan prints each frame's candidates, and with --probe reads a list
of coordinates back out of every frame. Run over 53 6D raws from 2015 to
2026, it found 32 persistent defects, 2 in 2015 and 32 by 2026, and showed
what a defect map has to account for: a frame that does not flag a
photosite proves nothing unless its neighbourhood is dark, and the 6D
hides some of its defects itself above ISO 5000. docs/dev/sensor-health.md
records the findings and the design they argue for.
2026-10-04 02:38:48 -04:00

83 lines
4.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# DarkRoom — Sensor health: a dated defect map per body
**Status:** Spike · 2026-10-04 · not built
**Companion to:** [requirements.md](requirements.md) FR-RAW-3, [catalog.md](catalog.md)
A sensor gains defective photosites as it ages, and a photosite that has gone bad does not recover.
This records, per camera body, which photosites are defective and since when, so that the library
can show how a sensor has aged and the hot-pixel repair can fix the defects a body is known to have
rather than only those that stand out in the frame at hand.
What exists is the measuring tool: `Demosaicer::find_hot_pixels` (the photosites the repair pass
would replace, without replacing them) and `core/dr-gpu/examples/sensor_scan.rs`, which prints them
per frame and, with `--probe`, reads a list of coordinates back out of each frame. The rest of this
document is what a spike with them on the 6D found, and the design it argues for.
---
## 1. What is wanted
- **Settings → Bodies**, one entry per body, with a graph of the defective share over time in two
series: photosites (the raw mosaic) and 2×2 cells holding at least one defective photosite (what
reaches a pixel of the output).
- **A dated defect map** per body, synced with the library like any other catalog data, and
cumulative: an entry is never removed.
- **The repair reads the map** whose date is nearest the frame's, and fixes every defect the body had
by then, whether or not it stands out in that frame.
- One body per model for now: the catalog stores `make model`, not a body serial.
## 2. What the spike found (6D, 2026-10-03)
53 CR2s, up to four per quarter at the highest ISO of a day, 2015 to 2026. The library holds almost
no 6D raws from 2016–2022, so onsets in that span are dated to the span, not the year. `sensor_scan`
ran at 0.8 s per frame, decode included.
**Persistence separates the sensor from the scene.** 4,179 photosites were flagged at least once;
3,554 on one day only (stars, glints, noise). A defect is a photosite that keeps coming back.
**A frame that does not flag a photosite is not evidence it was clean.** The repair's test is
relative to the neighbourhood, so a defect in a lit area does not stand out. Confirmed defects were
flagged in a median 20 % of the frames after their first sighting. Only a frame whose neighbourhood
at that photosite is dark counts, either way.
**The 6D hides some defects itself at high ISO.** (2517, 3172) reads 8,000–13,000 over neighbours
near 200 at ISO 2000–5000, and does not stand out at all at ISO 6400–12800 (82 over 149 on
2025-03-15). The camera appears to map out photosites it knows at those gains. So evidence for this
body comes from ISO ≤ 5000; the cut-off must be learned per body, not fixed.
**Long exposures light everything.** A 9.8 s frame saturated every candidate; it confirms, it does
not date.
**The curve.** Counting a photosite as defective from the first frame where it stands out, provided
it stands out in at least 60 % of the observable frames after that (32 defects; 31 with a clean
observable frame before onset to bracket it):
| Year | Defects | Share of photosites |
|---|---|---|
| 2015 | 2 | 0.1 ppm |
| 2016–2021 | 2 | 0.1 ppm |
| 2022 | 7 | 0.3 ppm |
| 2023 | 26 | 1.3 ppm |
| 2026 | 32 | 1.6 ppm |
(2517, 3172) is the shape every entry should have: clean at ISO 800–1000 in 2015 and at ISO 100–200
in 2016, then 338 over 72 at ISO 100 on 2022-08-13 and in every comparable frame since.
Weak defects exist too, about twice their neighbours ((1814, 3039)); the blind repair misses them in
most frames. A known map would catch them.
## 3. Design it argues for
- **Evidence per frame, per known photosite**: observable (dark neighbourhood, ISO inside the
body's band) and, if so, lit or clean. Not just the frame's flagged list.
- **A map entry is a bracket**: last clean observation, first lit observation, strength, kind. Onset
lies between the two; the graph plots it at the first, and can show the bracket.
- **The repair**: every entry whose first lit date is on or before the frame's capture date; for an
entry whose bracket contains the date, probe the photosite in the frame itself.
- **Storage and sync**: catalog tables created on first use (as `albums` does), so no schema bump
breaks an older peer. They travel in the snapshot by default. Merge is a set union of photosites
per body, the earlier first-lit and the later last-clean winning, which makes it commutative and
keeps the map cumulative.
- **Work**: a sample, not the library. Frames are picked for what they can reveal (dark, mid ISO,
long exposures), a few per body per month, and after the first pass only new imports are read.