Hi, I found a problem where the Lux metadata is always stuck at 400.0 when using --immediate with --awbgains set to non-zero values. I've been running with rpicam-still --immediate --awbgains 0,0 --shutter ... --gain ... and Lux was always correct. When I switched to --awbgains 1.3,1.6, Lux became permanently stuck at 400.0.
Setup:
- Pi 5, HQ camera (IMX477 NoIR)
rpicam-still --immediate --nopreview --raw --shutter 2155 --gain 1 --awbgains 1.3,1.6 --denoise off --sharpness 0
- libcamera
0.7.1+rpt20260429-1
- Running
LIBCAMERA_LOG_LEVELS=RPiLux:DEBUG produces zero RPiLux output, confirming Lux::process() never executes
I don't know cpp well so I asked Claude to check source code and it found what appears to be the following problem. I'd appreciate confirmation if this analysis is correct.
Root cause:
When --awbgains is set to non-zero values, Awb::setManualGains() disables auto AWB. This causes Awb::getConvergenceFrames() to return 0 (awb.cpp:192-201). Combined with manual exposure and gain, AgcChannel::getConvergenceFrames() also returns 0.
In ipa_base.cpp, startupFrameCount is calculated as:
result->startupFrameCount = std::max({ agcConvergenceFrames, awbConvergenceFrames });
With both at 0, startupFrameCount = 0. This means no frames are tagged as FrameStartup in the pipeline, and the very first frame is returned as FrameSuccess.
rpicam-apps drops FrameStartup frames (rpicam_app.cpp:1134-1136), so with --immediate this first FrameSuccess frame is captured immediately.
However, mistrustCount_ is still 1 (from CamHelper::mistrustFramesStartup()). In ipa_base.cpp:527:
if (processPending_ && frameCount_ >= mistrustCount_)
On frame 0, 0 >= 1 is false, so controller_.process() is skipped entirely. Lux::process() never runs. The reported Lux value is the constructor default of 400 (lux.cpp:29).
Why --awbgains 0,0 worked: Awb::setManualGains() checks for zero values and switches back to auto mode (awb.cpp:209). Auto AWB returns convergenceFrames = 3, so startupFrameCount becomes 4 (3 + mistrustCount). Those 4 frames get dropped as FrameStartup, and by the time the first FrameSuccess arrives, frameCount_ is well past mistrustCount_ and Lux::process() has run multiple times.
Impact: This affects any single-frame --immediate capture with fully manual controls (shutter + gain + awbgains). This is common in astrophotography and scientific imaging where exposures can be minutes long, making it impractical to throw away extra frames just to let lux process.
Possible fix: Lux is a passive read-only estimate that doesn't feed back into any control loop. There's no reason to gate it behind the mistrustCount_ guard — the ISP Y statistics are valid from frame 0. Exempting Lux::process() from the mistrust check would fix this without affecting any other algorithm.
Thanks for looking into it and for your help!
Hi, I found a problem where the Lux metadata is always stuck at 400.0 when using
--immediatewith--awbgainsset to non-zero values. I've been running withrpicam-still --immediate --awbgains 0,0 --shutter ... --gain ...and Lux was always correct. When I switched to--awbgains 1.3,1.6, Lux became permanently stuck at 400.0.Setup:
rpicam-still --immediate --nopreview --raw --shutter 2155 --gain 1 --awbgains 1.3,1.6 --denoise off --sharpness 00.7.1+rpt20260429-1LIBCAMERA_LOG_LEVELS=RPiLux:DEBUGproduces zero RPiLux output, confirmingLux::process()never executesI don't know cpp well so I asked Claude to check source code and it found what appears to be the following problem. I'd appreciate confirmation if this analysis is correct.
Root cause:
When
--awbgainsis set to non-zero values,Awb::setManualGains()disables auto AWB. This causesAwb::getConvergenceFrames()to return 0 (awb.cpp:192-201). Combined with manual exposure and gain,AgcChannel::getConvergenceFrames()also returns 0.In
ipa_base.cpp,startupFrameCountis calculated as:result->startupFrameCount = std::max({ agcConvergenceFrames, awbConvergenceFrames });With both at 0,
startupFrameCount= 0. This means no frames are tagged asFrameStartupin the pipeline, and the very first frame is returned asFrameSuccess.rpicam-apps drops
FrameStartupframes (rpicam_app.cpp:1134-1136), so with--immediatethis firstFrameSuccessframe is captured immediately.However,
mistrustCount_is still 1 (fromCamHelper::mistrustFramesStartup()). Inipa_base.cpp:527:if (processPending_ && frameCount_ >= mistrustCount_)On frame 0,
0 >= 1is false, socontroller_.process()is skipped entirely.Lux::process()never runs. The reported Lux value is the constructor default of 400 (lux.cpp:29).Why
--awbgains 0,0worked:Awb::setManualGains()checks for zero values and switches back to auto mode (awb.cpp:209). Auto AWB returnsconvergenceFrames = 3, sostartupFrameCountbecomes 4 (3 + mistrustCount). Those 4 frames get dropped asFrameStartup, and by the time the firstFrameSuccessarrives,frameCount_is well pastmistrustCount_andLux::process()has run multiple times.Impact: This affects any single-frame
--immediatecapture with fully manual controls (shutter + gain + awbgains). This is common in astrophotography and scientific imaging where exposures can be minutes long, making it impractical to throw away extra frames just to let lux process.Possible fix: Lux is a passive read-only estimate that doesn't feed back into any control loop. There's no reason to gate it behind the
mistrustCount_guard — the ISP Y statistics are valid from frame 0. ExemptingLux::process()from the mistrust check would fix this without affecting any other algorithm.Thanks for looking into it and for your help!