| Message ID | 20261017153829.442320-3-xiaqinghuan@mail.ustc.edu.cn |
|---|---|
| State | Not Applicable |
| Headers | show |
| Series |
|
| Related | show |
Hi 2026. 10. 17. 17:38 keltezéssel, Qianhui Xu írta: > The simple IPA AGC targets the middle of the histogram range, i.e. a mean > sample value of 0.5. As the debayer step gamma-encodes its output (the > default gamma is 2.2), such a target maps to roughly 73% of the full code > range on output, which looks washed out and clips highlights in scenes > with a high dynamic range. > > Allow platforms to override the target from the tuning file with a new > "exposureTarget" parameter, expressed as a fraction of the histogram > range. The default keeps the existing behaviour, so existing tuning files > keep their meaning. Please base changes on the master branch. This part has been considerably changed since the latest release, and thus the diff below is no longer applicable. > > Signed-off-by: Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> > --- > src/ipa/simple/algorithms/agc.cpp | 24 +++++++++++++++++++----- > src/ipa/simple/algorithms/agc.h | 4 ++++ > 2 files changed, 23 insertions(+), 5 deletions(-) > > diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp > index 2f7e040..b6e9d30 100644 > --- a/src/ipa/simple/algorithms/agc.cpp > +++ b/src/ipa/simple/algorithms/agc.cpp > @@ -25,10 +25,11 @@ namespace ipa::soft::algorithms { > static constexpr unsigned int kExposureBinsCount = 5; > > /* > - * The exposure is optimal when the mean sample value of the histogram is > - * in the middle of the range. > + * The exposure is optimal when the mean sample value of the histogram matches > + * the configured target. 0.5 (the middle of the range) is a bright default; > + * sensors tuned for photographic output usually target a lower code value. > */ > -static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; > +static constexpr double kDefaultExposureTarget = 0.5; > > /* > * This implements the hysteresis for the exposure adjustment. > @@ -38,9 +39,21 @@ static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; > static constexpr float kExposureSatisfactory = 0.2; > > Agc::Agc() > + : exposureTarget_(kDefaultExposureTarget) > { > } > > +int Agc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) > +{ > + const ValueNode &target = tuningData["exposureTarget"]; > + > + exposureTarget_ = target.get<double>(kDefaultExposureTarget); > + LOG(IPASoftExposure, Debug) > + << "Exposure target " << exposureTarget_; > + > + return 0; > +} > + > void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV) > { > /* > @@ -53,8 +66,9 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou > > int32_t &exposure = frameContext.sensor.exposure; > double &again = frameContext.sensor.gain; > + const double optimal = exposureTarget_ * kExposureBinsCount; > > - if (exposureMSV < kExposureOptimal - kExposureSatisfactory) { > + if (exposureMSV < optimal - kExposureSatisfactory) { > if (exposure < context.configuration.agc.exposureMax) { > int32_t next = exposure * kExpNumeratorUp / kExpDenominator; > if (next - exposure < 1) > @@ -70,7 +84,7 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou > } > } > > - if (exposureMSV > kExposureOptimal + kExposureSatisfactory) { > + if (exposureMSV > optimal + kExposureSatisfactory) { > if (again > context.configuration.agc.again10) { > double next = again * kExpNumeratorDown / kExpDenominator; > if (again - next < context.configuration.agc.againMinStep) > diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h > index 112d9f5..8d3d2f6 100644 > --- a/src/ipa/simple/algorithms/agc.h > +++ b/src/ipa/simple/algorithms/agc.h > @@ -19,6 +19,8 @@ public: > Agc(); > ~Agc() = default; > > + int init(IPAContext &context, const ValueNode &tuningData) override; > + > void process(IPAContext &context, const uint32_t frame, > IPAFrameContext &frameContext, > const SwIspStats *stats, > @@ -26,6 +28,8 @@ public: > > private: > void updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV); > + > + double exposureTarget_; > }; > > } /* namespace ipa::soft::algorithms */
diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index 2f7e040..b6e9d30 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -25,10 +25,11 @@ namespace ipa::soft::algorithms { static constexpr unsigned int kExposureBinsCount = 5; /* - * The exposure is optimal when the mean sample value of the histogram is - * in the middle of the range. + * The exposure is optimal when the mean sample value of the histogram matches + * the configured target. 0.5 (the middle of the range) is a bright default; + * sensors tuned for photographic output usually target a lower code value. */ -static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; +static constexpr double kDefaultExposureTarget = 0.5; /* * This implements the hysteresis for the exposure adjustment. @@ -38,9 +39,21 @@ static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; static constexpr float kExposureSatisfactory = 0.2; Agc::Agc() + : exposureTarget_(kDefaultExposureTarget) { } +int Agc::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData) +{ + const ValueNode &target = tuningData["exposureTarget"]; + + exposureTarget_ = target.get<double>(kDefaultExposureTarget); + LOG(IPASoftExposure, Debug) + << "Exposure target " << exposureTarget_; + + return 0; +} + void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV) { /* @@ -53,8 +66,9 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou int32_t &exposure = frameContext.sensor.exposure; double &again = frameContext.sensor.gain; + const double optimal = exposureTarget_ * kExposureBinsCount; - if (exposureMSV < kExposureOptimal - kExposureSatisfactory) { + if (exposureMSV < optimal - kExposureSatisfactory) { if (exposure < context.configuration.agc.exposureMax) { int32_t next = exposure * kExpNumeratorUp / kExpDenominator; if (next - exposure < 1) @@ -70,7 +84,7 @@ void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, dou } } - if (exposureMSV > kExposureOptimal + kExposureSatisfactory) { + if (exposureMSV > optimal + kExposureSatisfactory) { if (again > context.configuration.agc.again10) { double next = again * kExpNumeratorDown / kExpDenominator; if (again - next < context.configuration.agc.againMinStep) diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h index 112d9f5..8d3d2f6 100644 --- a/src/ipa/simple/algorithms/agc.h +++ b/src/ipa/simple/algorithms/agc.h @@ -19,6 +19,8 @@ public: Agc(); ~Agc() = default; + int init(IPAContext &context, const ValueNode &tuningData) override; + void process(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, const SwIspStats *stats, @@ -26,6 +28,8 @@ public: private: void updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV); + + double exposureTarget_; }; } /* namespace ipa::soft::algorithms */
The simple IPA AGC targets the middle of the histogram range, i.e. a mean sample value of 0.5. As the debayer step gamma-encodes its output (the default gamma is 2.2), such a target maps to roughly 73% of the full code range on output, which looks washed out and clips highlights in scenes with a high dynamic range. Allow platforms to override the target from the tuning file with a new "exposureTarget" parameter, expressed as a fraction of the histogram range. The default keeps the existing behaviour, so existing tuning files keep their meaning. Signed-off-by: Qianhui Xu <xiaqinghuan@mail.ustc.edu.cn> --- src/ipa/simple/algorithms/agc.cpp | 24 +++++++++++++++++++----- src/ipa/simple/algorithms/agc.h | 4 ++++ 2 files changed, 23 insertions(+), 5 deletions(-)