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.
4.6 KiB
DarkRoom — Sensor health: a dated defect map per body
Status: Spike · 2026-10-04 · not built Companion to: requirements.md FR-RAW-3, 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
albumsdoes), 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.