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.
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user