fwt_software/docs/raw12-pipeline-measurements...

14 KiB
Raw Blame History

Context for discussing raw12-pipeline-measurements.md

This file gives an AI assistant the background needed to discuss the report raw12-pipeline-measurements.md: what was asked, how the tests were run, what the numbers mean, what was decided, which points have been questioned, and what is still open. Read the report itself for the full tables and figures; this file explains around them. State as of 2026-10-05, branch raw12-capture.

1. The system in one paragraph

Fire-watch towers scan waypoints with a gimbal-mounted Allied Vision Alvium camera and keep one picture per waypoint, saved as JPEG XL on the tower's computer. After the gimbal stops, the camera's continuous auto-exposure needs frames to adapt to the new view, so the scan waits ("settling"). The tower program is C++ (fgc), using Vimba X (VmbCPP), OpenCV and libjxl. Two computers are in use: LattePanda IOTA (Intel N150, tower 121) and LattePanda 3 Delta (Celeron N5105, tower 111).

2. The question and how it evolved

  • Origin: speeding up the per-waypoint capture. Today the camera sends RGB8 (colour made in the camera, 48.6 MB per full frame), which limits the frame rate over USB to 4.95 fps at the agreed 250 MB/s link cap, so exposure settles slowly.
  • Proposal tested: camera sends raw BayerRG12p (24.3 MB, 9.86 fps), the tower unpacks, de-Bayers (converts to colour) and saves 12-bit colour JPEG XL.
  • An earlier campaign (camera-capture-measurements.md) used blurred dim scenes; its compression numbers were unrepresentative. This campaign used a focused, real scene (spruce, birch, leaves, building, ~13% clipped sky).
  • The report went through several rounds of requested edits: example images per compression type, methodology detail (how settings were forced), processing on one identical frame on both towers, an abstract, a pixel-format description, an academic structure (numbered sections, figure and table captions, units in every header, references), captions that explain every term, an explanation of raw mosaic vs raw planes, a more careful treatment of what 12 bits add, and a fuller explanation of the keep rules.

3. User decisions (fixed constraints, do not re-litigate)

  • Full resolution always; no binning; one quality setting (no switching between a fast low quality mode and a slow high quality one); continuous acquisition.
  • No exposure memory across passes or from neighbouring waypoints.
  • USB link cap 250 MB/s (DeviceLinkThroughputLimit), conservative.
  • BayerRG12p everywhere, day and night profiles.
  • Save 12-bit colour JPEG XL only; no raw-planes option in the program for now.
  • Fixed white balance ratios kept in the profiles (auto white balance was measured but not adopted).
  • When saving falls behind, pause the scan (save gate) rather than drop frames.
  • Writing style for the report: academic structure, captions that explain their terms, units given wherever possible, plain language. Commit messages: one plain-language line, no trailers.

4. Test setup essentials

  • Same camera for both towers: Alvium 1800 U-1620c DEV_1AB22C0B7D4E, 5328 × 3040, firmware 14.3. Moved from 121 to 111 so only the computer differs. Tower 111's own camera (1800 U-1240c, 12 MP, 4024 × 3036) was not tested; its frames are 25% smaller, so the Delta results are conservative.
  • Dates: tower 121 on 2 Oct 2026 (sunny afternoon); tower 111 on 4 Oct (afternoon into evening); shared-frame processing on both towers simultaneously on 4 Oct 23:03–23:32.
  • The tower program was stopped during all tests.
  • Storage write speed: IOTA 218 MB/s, Delta 76 MB/s (eMMC). Libraries: 121 libjxl 0.11.1 / OpenCV 4.10; 111 libjxl 0.11.0 / OpenCV 4.5.4.
  • Tools in tools/camera-bench/ (camera_bench.py with vmbpy; raw12_bench.cpp; run scripts; plot_results.py, plot_common.py, make_figures.py, compare_examples.py). Results in results-2026-10-02-tower121/, results-2026-10-04-tower111/, results-common-tower121/111/. Full-size example .jxl files in tools/camera-bench/examples-* (git-ignored, ~115 MB per set). The recorded auto-exposure steps are also in tests/data/ae_steps.csv (used by a unit test).

5. Methods that are easy to misunderstand

  • Forcing settings: feature writes over USB while streaming. ExposureAuto/GainAuto set to Off freezes current values, then ExposureTime/Gain are written; for the 2 s "force" phase the camera delivers 10–30 frames at the forced values; then auto is set back to Continuous (time 0). Dark step: exposure E₀/16 with 0 dB gain. Bright step: exposure at the 20 ms cap plus gain up to +24.1 dB (×16). Bright start is fully clipped (97–100%), so the camera cannot see how far over it is.
  • Brightness = mean of a sparse sample of the frame buffer on 0–255 (12-bit: high byte). Settled = stays within ±10% of the median brightness of the run's last 2 s.
  • Frames in transit: the first 1–5 frames after release still carry the forced values.
  • Shared frame: tower 121's real frame rebuilt bit-exactly from its lossless 12-bit file and copied to both towers (identical MD5). Its 8-bit raw version is the 12-bit value >> 4; its "RGB8" version is the tower's own edge-aware conversion scaled to 8 bits, so on the shared frame the RGB8 file size equals raw 8-bit's (2.66 MB). The camera's real RGB8 files were 3.7–4.0 MB.
  • Synthetic frame: "dead leaves" texture plus noise (ISO 19567-2 style), sharper than any real scene; worst case for the encoder; used for pile-up and heat.
  • Quality is against the exact lossless 12-bit colour image of the same frame: PSNR on the 12-bit scale over 3 channels; SSIM on luma. JPEG XL is perceptual, so PSNR is moderate while SSIM is high.
  • Camera BayerRG = OpenCV COLOR_BayerBG2RGB (verified). The camera applies white balance to raw.
  • 12-bit JPEG XL from 16-bit words needs bit depth "from codestream", or values get rescaled; lossless needs the original colour profile. An earlier mistake on both points was found and the 12-bit numbers corrected before the final report.

6. Key results (numbers to quote)

Frame rate at 250 MB/s (fps): RGB8 4.95, BayerRG8 14.75, BayerRG10p 11.81, BayerRG12p 9.86, BayerRG12 7.41. 60 s streams: zero lost or incomplete frames in every format on both towers, also while saving continuously. On 111 the camera shares the USB 3 controller with a 5G modem, a USB network adapter and the idle second camera.

Auto-exposure settling after ×16 steps: ~25–35 frames, so time scales with 1/frame rate. BayerRG8 1.8–2.7 s; BayerRG12p 2.2–6.6 s (typically ≈ 3 s; long runs contain mid-way pauses); RGB8 4.6–6.6 s. With exposure at its 20 ms cap the camera adjusts gain by 1–2 dB per frame and sometimes pauses up to 2.8 s while still far too bright.

Keep rules (24 runs = 3 formats × 2 directions × 2 repeats × 2 towers; good = within ±10% of final level):

  • "Exposure and gain unchanged 3 frames": 0/24. Replay detail (added 2026-10-05 from tests/data/ae_steps.csv): 16 runs fired on the first frames (0.2–0.5 s, frames in transit, image black 9–13 or white 253–255 vs ≈ 120); 8 fired during mid-way pauses at brightness 33–227.
  • "In band 2 frames": 12/24 (passes through the band ~15% above final level when descending).
  • "In band and steady (≤ 2%/frame) 3 frames": 21/24 (3 slightly early on descents); median time when correct 3.61 s (121) / 5.45 s (111). Implemented as settle_rule = brightness.
  • Band = IntensityControllerTarget ± Tolerance = 50 ± 5% → 114.75–140.25 on 0–255.

Auto white balance: recovers in 2–4 frames (0.2–0.8 s), stable ±0.8–3.1%. Exposure, not white balance, sets the wait. Auto ratios ≈ red 2.36–2.46, blue 1.87–1.97 vs fixed profile 2.87 / 2.0.

Processing on the shared frame (4 threads; IOTA / Delta, ms): unpack 10 / 17; de-Bayer 12-bit bilinear 17 / 22, edge-aware 28 / 33; encode 12-bit d1.0 e3 480 / 765; today's RGB8 encode d0.8 e4 566 / 867. Totals: 12-bit edge-aware d1.0 e3 518 / 820 ms, 2.65 MB; today RGB8 567 / 872 ms, 3.27 MB; raw 8-bit → colour 468 / 741 ms, 2.66 MB. Delta ≈ 1.6× slower. Unpack + de-Bayer = 7% of a save; encode ≈ 90%. Effort 1–4 within ~15%; effort 3 smallest; 4 threads fastest; on the IOTA 3 threads is oddly slower than 2.

Distance sweep (12-bit colour, shared frame, MB): lossless 44.8, d0.3 7.19, d0.5 5.15, d0.8 3.16, d1.0 2.65, d1.5 1.63, d2.0 1.32. Quality at d1.0: PSNR 36.6 dB, SSIM 0.973. Raw planes d0.5: 6.0 MB, PSNR 47.2 dB. Lossless: raw planes 17.6, raw mosaic 20.9, colour 47.4 MB.

Throughput (saves/s back to back, shared frame): 1 worker × 4 threads IOTA 1.79, Delta 1.19; 2 × 2: 2.15 / 1.53. On the shared frame the IOTA keeps up with one frame per 0.7 s with one worker; the Delta needs two. Pile-up details on the synthetic frame are in Table 18.

Heat (10 min each): one frame every 2 s → IOTA 53 °C mean / 5.1 W, Delta 66 °C / 5.7 W. Back to back (2 × 2): IOTA 80 °C, 12.5 W, loses 16% throughput as it heats; Delta 78 °C, at its 8 W limit, steady.

Storage: 12-bit colour d1.0 ≈ 2.65–2.75 MB/frame → 114–119 GB/day at one frame every 2 s. Towers have 11–22 GB free, so images must leave the tower continuously.

7. The 12-bit question (discussed at length; current position)

  • Noise floor (12-bit DN, green pixels): at ≈ 700 DN 26 (0 dB) / 42 (15 dB); at ≈ 1500 35 / 58; at ≈ 3000 37 / 80. One 8-bit step = 16 DN; rounding error RMS = 16/√12 = 4.6 DN.
  • Errors add in quadrature: √(26² + 4.6²) = 26.4 → rounding raises noise by at most 1.5% in the measured daylight scene (0.6% at 15 dB, < 0.2% in bright areas).
  • Averaging: while noise > one 8-bit step, rounding error is random (dither) and averages down at the same √N rate as noise, so 8-bit averages resolve sub-step differences as well as 12-bit. A simulation (not in the report, by user request) showed a 3 DN difference over 100 × 100 pixels recovered exactly by both at noise 26, but only 0.2 of 3 by 8-bit at noise 2.
  • 12 bits always contain at least as much information; at noise 26 the dropped bits carry about 0.02 bits/pixel of scene information.
  • Compression dominates: PSNR 36.6 dB at d1.0 ≈ 61 DN RMS error (13× the rounding error); d0.3 ≈ 40 DN; raw planes d0.5 ≈ 18 DN. Much of that is discarded noise, and JPEG XL keeps relatively more precision in dark tones; shadow-only error was not measured.
  • Where 12 bits should matter (Table 17b): dark areas at low gain where noise < one 8-bit step (below ≈ 300 DN at 0 dB, ≈ 100 DN at 15 dB), i.e. high-contrast scenes such as shaded forest or backlit ridges under a bright sky at dawn or dusk; thin smoke over dark terrain is the relevant fire-detection case. At night with high gain, noise is amplified above the step again, so little benefit. Strong shadow lifting (≥ 3 stops) untested; 2 stops showed no difference.
  • None of the dark cases were measured (the darkest area in the scene was ≈ 700 DN; a dusk attempt on 121 needed 24 dB and was discarded). Proposed test: a dawn/dusk frame with dark terrain under a bright sky, compared as in Figures 12 and 13.
  • 12-bit vs 8-bit raw costs little in saving (same size, ~10% more time) but the camera runs at 9.86 vs 14.75 fps, ~1 s slower settling in daylight. The main gain over today is frame rate, not bit depth.

8. Limitations to keep in mind

One scene, two afternoons, main runs at ≈ 15 dB gain plus one 0 dB frame (124 ms exposure, cap raised to 200 ms); no night data. Steps forced on a still camera, not real slews; real waypoint-to-waypoint changes are usually smaller. Tower program stopped, so its own load is not included. Detection quality not measured (PSNR/SSIM stand in). Slew time (≈ 1–1.5 s) and mechanical settling (≈ 0.2 s) in the pace table are estimates.

9. Implementation status (branch raw12-capture)

Implemented and unit-tested (457 tests pass locally): 12-bit JPEG XL encoding with the bit-depth fix and true lossless (known issue #20 fixed); frames carry pixel layout, chunk exposure/gain/white balance and a brightness sample; ImagePipeline unpacks and de-Bayers (edge-aware default), N workers, bounded queue; scheduler save gate pauses the scan when the queue is full; SettleRule (settle_rule = brightness) with a time limit and an "at limit" exit; config keys save_workers, jxl_threads, save_queue_max, demosaic, settle_rule; profile export script recipe for raw standard and night profiles. Plan: raw12-pipeline-implementation-plan.md.

Not done yet: exporting the new camera profiles on a tower (the 1620c is currently on tower 111 and must be moved back to 121 first); field verification with real slews; checking that downstream consumers decode 12-bit files; the dawn/dusk 12-bit test. The branch is deployed on tower 111, whose boot service still passes old --init/--start flags and needs scripts/install-tower.

10. Report structure (for orientation)

Abstract; Units and abbreviations; 1 Introduction (1.1 question, 1.2 pixel formats incl. raw mosaic vs raw planes, Figures 1a/1b, Table 1; 1.3 earlier work); 2 Materials and methods (2.1–2.12, Tables 2–8, Figure 2); 3 Results (3.1 frame rate, 3.2 auto-exposure, 3.3 keep rules, 3.4 white balance, 3.5 processing per step, 3.6 effort and threads, 3.7 quality setting, 3.8 example images, 3.9 information content of 12-bit data with Tables 17a/17b, 3.10 throughput, 3.11 heat, 3.12 storage; Figures 3–17, Tables 9–20); 4 Discussion (summary Table 21, pace Table 22, limitations); 5 Conclusions and six recommendations; References [1]–[7]; Appendix A raw data and tools; Appendix B per-tower processing times. Figures live in docs/images/raw12/.