| Message ID | 20260803131435.153927-49-barnabas.pocze@ideasonboard.com |
|---|---|
| State | Superseded |
| Headers | show |
| Series |
|
| Related | show |
Barnabás Pőcze <barnabas.pocze@ideasonboard.com> writes: > Use the `AgcAlgorithm` class to implement the agc algorithm. > > Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> > --- > src/ipa/libipa/agc.cpp | 14 ++-- > src/ipa/simple/algorithms/agc.cpp | 134 +++++++++++++++++------------- > src/ipa/simple/algorithms/agc.h | 12 ++- > src/ipa/simple/ipa_context.h | 15 +--- > src/ipa/simple/soft_simple.cpp | 55 +----------- > 5 files changed, 99 insertions(+), 131 deletions(-) > > diff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp > index bb6891cf51..169a53f2cd 100644 > --- a/src/ipa/libipa/agc.cpp > +++ b/src/ipa/libipa/agc.cpp > @@ -707,14 +707,12 @@ void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state, > metadata.set(controls::ExposureTime, > utils::Duration(lineDuration * frameContext.exposure).get<std::micro>()); > metadata.set(controls::FrameDuration, frameContext.frameDuration.get<std::micro>()); > - metadata.set(controls::ExposureTimeMode, > - frameContext.autoExposureEnabled > - ? controls::ExposureTimeModeAuto > - : controls::ExposureTimeModeManual); > - metadata.set(controls::AnalogueGainMode, > - frameContext.autoGainEnabled > - ? controls::AnalogueGainModeAuto > - : controls::AnalogueGainModeManual); > + metadata.set(controls::ExposureTimeMode, frameContext.autoExposureEnabled > + ? controls::ExposureTimeModeAuto > + : controls::ExposureTimeModeManual); > + metadata.set(controls::AnalogueGainMode, frameContext.autoGainEnabled > + ? controls::AnalogueGainModeAuto > + : controls::AnalogueGainModeManual); Related? > > metadata.set(controls::AeExposureMode, frameContext.exposureMode); > metadata.set(controls::AeConstraintMode, frameContext.constraintMode); > diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp > index 7b980c7382..9926b3c593 100644 > --- a/src/ipa/simple/algorithms/agc.cpp > +++ b/src/ipa/simple/algorithms/agc.cpp > @@ -13,30 +13,76 @@ > > #include <libipa/histogram.h> > > -#include "control_ids.h" > - > namespace libcamera { > > LOG_DEFINE_CATEGORY(IPASoftExposure) > > namespace ipa::soft::algorithms { > > +namespace { > + > +class AgcTraits : public AgcMeanLuminance::Traits > +{ > +public: > + AgcTraits(const Histogram &yHist) > + : yHist_(yHist) > + { > + } > + > + double estimateLuminance(double gain) const override > + { > + /* > + * TODO: Improve by asking the weight of saturating and non-saturating > + * bins directly from the histogram > + */ > + double sum = 0; > + > + for (size_t i = 0; i < yHist_.bins(); i++) > + sum += std::min<double>(yHist_.bins(), i * gain) * yHist_[i]; > + > + return sum / yHist_.total() / yHist_.bins(); > + } > + > +private: > + const Histogram &yHist_; > +}; > + > +} /* namespace */ > + > +int Agc::init(IPAContext &context, const ValueNode &tuningData) > +{ > + int ret = agc_.init(tuningData, context.camHelper.get()); > + if (ret) > + return ret; > + > + return agc_.configure(context.configuration.agc, context.activeState.agc, { > + .sensorInfo = context.sensorInfo, > + .sensorControls = context.sensorControls, > + .ctrlMap = context.ctrlMap, > + .autoAllowed = true, > + }); Why is it here? Isn't the same call in Agc::configure sufficient? > +} > + > int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) > { > - agc_.setLimits({ > - .exposure = { > - context.configuration.agc.exposureMin, > - context.configuration.agc.exposureMax, > - }, > - .gain = { > - context.configuration.agc.againMin, > - context.configuration.agc.againMax, > - }, > - .gainMinStep = context.configuration.agc.againMinStep, > - .gain1 = context.configuration.agc.again10, > + return agc_.configure(context.configuration.agc, context.activeState.agc, { > + .sensorInfo = context.sensorInfo, > + .sensorControls = context.sensorControls, > + .ctrlMap = context.ctrlMap, > + .autoAllowed = true, > }); > +} > + > +void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, > + IPAFrameContext &frameContext, const ControlList &controls) > +{ > + agc_.queueRequest(context.configuration.agc, context.activeState.agc, frameContext.agc, controls); > +} > > - return 0; > +void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, > + IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) > +{ > + agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc); > } > > void Agc::process(IPAContext &context, > @@ -45,49 +91,25 @@ void Agc::process(IPAContext &context, > const SwIspStats *stats, > ControlList &metadata) > { > - utils::Duration exposureTime = > - context.configuration.agc.lineDuration * frameContext.sensor.exposure; > - metadata.set(controls::ExposureTime, exposureTime.get<std::micro>()); > - metadata.set(controls::AnalogueGain, frameContext.sensor.gain); > - > - if (!context.activeState.agc.valid) { > - /* > - * Init active-state from sensor values in case updateExposure() > - * does not run for the first frame. > - */ > - context.activeState.agc.exposure = frameContext.sensor.exposure; > - context.activeState.agc.again = frameContext.sensor.gain; > - context.activeState.agc.valid = true; > - } > - > - if (!stats->valid) { > - /* > - * Use the new exposure and gain values calculated the last time > - * there were valid stats. > - */ > - frameContext.agc.exposure = context.activeState.agc.exposure; > - frameContext.agc.gain = context.activeState.agc.again; > - return; > + if (stats->valid) { > + auto histogram = stats->yHistogram; > + > + const unsigned int blackLevelHistIdx = > + context.activeState.blc.level * std::size(histogram) / 256; > + for (size_t i = 1; i < blackLevelHistIdx; i++) ^^^^^^ This type should be used already at the place this loop was introduced. > + histogram[0] += std::exchange(histogram[i], 0); > + > + Histogram yHist(histogram); > + > + agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {{ > + .traits = AgcTraits(yHist), > + .yHist = yHist, > + .exposure = frameContext.sensor.exposure, > + .gain = frameContext.sensor.gain, > + }}, metadata); > + } else { > + agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {}, metadata); > } > - > - auto histogram = stats->yHistogram; > - const unsigned int blackLevelHistIdx = > - context.activeState.blc.level * std::size(histogram) / 256; > - > - for (unsigned int i = 1; i < blackLevelHistIdx; i++) > - histogram[0] += std::exchange(histogram[i], 0); > - > - const auto &newEv = agc_.calculateNewEv({ > - .yHist = { histogram }, > - .exposure = frameContext.sensor.exposure, > - .gain = frameContext.sensor.gain, > - }); > - > - frameContext.agc.exposure = newEv.exposure; > - frameContext.agc.gain = newEv.analogueGain; > - > - context.activeState.agc.exposure = frameContext.agc.exposure; > - context.activeState.agc.again = frameContext.agc.gain; > } > > REGISTER_IPA_ALGORITHM(Agc, "Agc") > diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h > index 2e156e135c..6e37ce6eff 100644 > --- a/src/ipa/simple/algorithms/agc.h > +++ b/src/ipa/simple/algorithms/agc.h > @@ -9,7 +9,7 @@ > > #include "algorithm.h" > > -#include <libipa/agc_msv.h> > +#include <libipa/agc.h> > > namespace libcamera { > > @@ -18,15 +18,23 @@ namespace ipa::soft::algorithms { > class Agc : public Algorithm > { > public: > + int init(IPAContext &context, const ValueNode &tuningData) override; > + > int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; > > + void queueRequest(IPAContext &context, const uint32_t frame, > + IPAFrameContext &frameContext, const ControlList &controls) override; > + > + void prepare(IPAContext &context, const uint32_t frame, > + IPAFrameContext &frameContext, DebayerParams *params) override; > + > void process(IPAContext &context, const uint32_t frame, > IPAFrameContext &frameContext, > const SwIspStats *stats, > ControlList &metadata) override; > > private: > - AgcMSV agc_; > + AgcAlgorithm agc_; > }; > > } /* namespace ipa::soft::algorithms */ > diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h > index 96b3c5b21d..63889cd8f6 100644 > --- a/src/ipa/simple/ipa_context.h > +++ b/src/ipa/simple/ipa_context.h > @@ -17,6 +17,7 @@ > #include "libcamera/internal/matrix.h" > #include "libcamera/internal/vector.h" > > +#include <libipa/agc.h> > #include <libipa/camera_sensor_helper.h> > #include <libipa/fc_queue.h> > > @@ -27,10 +28,7 @@ namespace libcamera { > namespace ipa::soft { > > struct IPASessionConfiguration { > - struct { > - uint32_t exposureMin, exposureMax; > - double againMin, againMax, again10, againMinStep; > - utils::Duration lineDuration; > + struct Agc : agc::Session { > } agc; > struct { > std::optional<uint8_t> level; > @@ -38,10 +36,7 @@ struct IPASessionConfiguration { > }; > > struct IPAActiveState { > - struct { > - uint32_t exposure; > - double again; > - bool valid; > + struct Agc : agc::ActiveState { > } agc; > > struct { > @@ -68,9 +63,7 @@ struct IPAActiveState { > struct IPAFrameContext : public FrameContext { > Matrix<float, 3, 3> ccm; > > - struct { > - uint32_t exposure; > - double gain; > + struct Agc : agc::FrameContext { > } agc; > > struct { > diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp > index 8b5df884d4..f35e269deb 100644 > --- a/src/ipa/simple/soft_simple.cpp > +++ b/src/ipa/simple/soft_simple.cpp > @@ -107,6 +107,7 @@ int IPASoftSimple::init(const IPASettings &settings, > } > > context_.sensorInfo = sensorInfo; > + context_.sensorControls = sensorControls; > > /* Load the tuning data file */ > File file(settings.configurationFile); > @@ -180,22 +181,6 @@ int IPASoftSimple::init(const IPASettings &settings, > ControlInfoMap::Map ctrlMap = context_.ctrlMap; > *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls); > > - /* > - * Check if the sensor driver supports the controls required by the > - * Soft IPA. > - * Don't save the min and max control values yet, as e.g. the limits > - * for V4L2_CID_EXPOSURE depend on the configured sensor resolution. > - */ > - if (sensorControls.find(V4L2_CID_EXPOSURE) == sensorControls.end()) { > - LOG(IPASoft, Error) << "Don't have exposure control"; > - return -EINVAL; > - } > - > - if (sensorControls.find(V4L2_CID_ANALOGUE_GAIN) == sensorControls.end()) { > - LOG(IPASoft, Error) << "Don't have gain control"; > - return -EINVAL; > - } > - > return 0; > } > > @@ -203,55 +188,17 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo, ControlInfoMap *ip > { > context_.sensorControls = configInfo.sensorControls; > > - const ControlInfo &exposureInfo = context_.sensorControls.find(V4L2_CID_EXPOSURE)->second; > - const ControlInfo &gainInfo = context_.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; > - > /* Clear the IPA context before the streaming session. */ > context_.configuration = {}; > context_.activeState = {}; > context_.frameContexts.clear(); > > - context_.configuration.agc.lineDuration = > - context_.sensorInfo.minLineLength * 1.0s / context_.sensorInfo.pixelRate; > - context_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>(); > - context_.configuration.agc.exposureMax = exposureInfo.max().get<int32_t>(); > - if (!context_.configuration.agc.exposureMin) { > - LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear"; > - context_.configuration.agc.exposureMin = 1; > - } > - > - int32_t againMin = gainInfo.min().get<int32_t>(); > - int32_t againMax = gainInfo.max().get<int32_t>(); > - int32_t againDef = gainInfo.def().get<int32_t>(); > - > - if (context_.camHelper) { > - context_.configuration.agc.againMin = context_.camHelper->gain(againMin); > - context_.configuration.agc.againMax = context_.camHelper->gain(againMax); > - context_.configuration.agc.again10 = std::max(context_.configuration.agc.againMin, 1.0); > - context_.configuration.agc.againMinStep = > - (context_.configuration.agc.againMax - > - context_.configuration.agc.againMin) / > - 100.0; > - } else { > - context_.configuration.agc.againMax = againMax; > - context_.configuration.agc.again10 = againDef; > - context_.configuration.agc.againMin = againMin; > - context_.configuration.agc.againMinStep = 1.0; > - } > - > for (const auto &algo : algorithms()) { > int ret = algo->configure(context_, configInfo); > if (ret) > return ret; > } > > - LOG(IPASoft, Info) > - << "Exposure " << context_.configuration.agc.exposureMin << "-" > - << context_.configuration.agc.exposureMax > - << ", gain " << context_.configuration.agc.againMin << "-" > - << context_.configuration.agc.againMax > - << " (" << context_.configuration.agc.againMinStep << ")"; > - > *ipaControls = { ControlInfoMap::Map(context_.ctrlMap), controls::controls }; > > return 0;
2026. 08. 04. 17:29 keltezéssel, Milan Zamazal írta: > Barnabás Pőcze <barnabas.pocze@ideasonboard.com> writes: > >> Use the `AgcAlgorithm` class to implement the agc algorithm. >> >> Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> >> --- >> src/ipa/libipa/agc.cpp | 14 ++-- >> src/ipa/simple/algorithms/agc.cpp | 134 +++++++++++++++++------------- >> src/ipa/simple/algorithms/agc.h | 12 ++- >> src/ipa/simple/ipa_context.h | 15 +--- >> src/ipa/simple/soft_simple.cpp | 55 +----------- >> 5 files changed, 99 insertions(+), 131 deletions(-) >> >> diff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp >> index bb6891cf51..169a53f2cd 100644 >> --- a/src/ipa/libipa/agc.cpp >> +++ b/src/ipa/libipa/agc.cpp >> @@ -707,14 +707,12 @@ void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state, >> metadata.set(controls::ExposureTime, >> utils::Duration(lineDuration * frameContext.exposure).get<std::micro>()); >> metadata.set(controls::FrameDuration, frameContext.frameDuration.get<std::micro>()); >> - metadata.set(controls::ExposureTimeMode, >> - frameContext.autoExposureEnabled >> - ? controls::ExposureTimeModeAuto >> - : controls::ExposureTimeModeManual); >> - metadata.set(controls::AnalogueGainMode, >> - frameContext.autoGainEnabled >> - ? controls::AnalogueGainModeAuto >> - : controls::AnalogueGainModeManual); >> + metadata.set(controls::ExposureTimeMode, frameContext.autoExposureEnabled >> + ? controls::ExposureTimeModeAuto >> + : controls::ExposureTimeModeManual); >> + metadata.set(controls::AnalogueGainMode, frameContext.autoGainEnabled >> + ? controls::AnalogueGainModeAuto >> + : controls::AnalogueGainModeManual); > > Related? Sadly not! > >> >> metadata.set(controls::AeExposureMode, frameContext.exposureMode); >> metadata.set(controls::AeConstraintMode, frameContext.constraintMode); >> diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp >> index 7b980c7382..9926b3c593 100644 >> --- a/src/ipa/simple/algorithms/agc.cpp >> +++ b/src/ipa/simple/algorithms/agc.cpp >> @@ -13,30 +13,76 @@ >> >> #include <libipa/histogram.h> >> >> -#include "control_ids.h" >> - >> namespace libcamera { >> >> LOG_DEFINE_CATEGORY(IPASoftExposure) >> >> namespace ipa::soft::algorithms { >> >> +namespace { >> + >> +class AgcTraits : public AgcMeanLuminance::Traits >> +{ >> +public: >> + AgcTraits(const Histogram &yHist) >> + : yHist_(yHist) >> + { >> + } >> + >> + double estimateLuminance(double gain) const override >> + { >> + /* >> + * TODO: Improve by asking the weight of saturating and non-saturating >> + * bins directly from the histogram >> + */ >> + double sum = 0; >> + >> + for (size_t i = 0; i < yHist_.bins(); i++) >> + sum += std::min<double>(yHist_.bins(), i * gain) * yHist_[i]; >> + >> + return sum / yHist_.total() / yHist_.bins(); >> + } >> + >> +private: >> + const Histogram &yHist_; >> +}; >> + >> +} /* namespace */ >> + >> +int Agc::init(IPAContext &context, const ValueNode &tuningData) >> +{ >> + int ret = agc_.init(tuningData, context.camHelper.get()); >> + if (ret) >> + return ret; >> + >> + return agc_.configure(context.configuration.agc, context.activeState.agc, { >> + .sensorInfo = context.sensorInfo, >> + .sensorControls = context.sensorControls, >> + .ctrlMap = context.ctrlMap, >> + .autoAllowed = true, >> + }); > > Why is it here? Isn't the same call in Agc::configure sufficient? It's really only there to populate `ctrlMap` to provide some info about the controls for a freshly initialized, not yet configured camera. This is not ideal, of course; maybe an IPA should do an initial configuration during `init()`, and then this wouldn't be needed, and controls could just be managed in `Algorithm::configure()`. > >> +} >> + >> int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) >> { >> - agc_.setLimits({ >> - .exposure = { >> - context.configuration.agc.exposureMin, >> - context.configuration.agc.exposureMax, >> - }, >> - .gain = { >> - context.configuration.agc.againMin, >> - context.configuration.agc.againMax, >> - }, >> - .gainMinStep = context.configuration.agc.againMinStep, >> - .gain1 = context.configuration.agc.again10, >> + return agc_.configure(context.configuration.agc, context.activeState.agc, { >> + .sensorInfo = context.sensorInfo, >> + .sensorControls = context.sensorControls, >> + .ctrlMap = context.ctrlMap, >> + .autoAllowed = true, >> }); >> +} >> + >> +void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, >> + IPAFrameContext &frameContext, const ControlList &controls) >> +{ >> + agc_.queueRequest(context.configuration.agc, context.activeState.agc, frameContext.agc, controls); >> +} >> >> - return 0; >> +void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, >> + IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) >> +{ >> + agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc); >> } >> >> void Agc::process(IPAContext &context, >> @@ -45,49 +91,25 @@ void Agc::process(IPAContext &context, >> const SwIspStats *stats, >> ControlList &metadata) >> { >> - utils::Duration exposureTime = >> - context.configuration.agc.lineDuration * frameContext.sensor.exposure; >> - metadata.set(controls::ExposureTime, exposureTime.get<std::micro>()); >> - metadata.set(controls::AnalogueGain, frameContext.sensor.gain); >> - >> - if (!context.activeState.agc.valid) { >> - /* >> - * Init active-state from sensor values in case updateExposure() >> - * does not run for the first frame. >> - */ >> - context.activeState.agc.exposure = frameContext.sensor.exposure; >> - context.activeState.agc.again = frameContext.sensor.gain; >> - context.activeState.agc.valid = true; >> - } >> - >> - if (!stats->valid) { >> - /* >> - * Use the new exposure and gain values calculated the last time >> - * there were valid stats. >> - */ >> - frameContext.agc.exposure = context.activeState.agc.exposure; >> - frameContext.agc.gain = context.activeState.agc.again; >> - return; >> + if (stats->valid) { >> + auto histogram = stats->yHistogram; >> + >> + const unsigned int blackLevelHistIdx = >> + context.activeState.blc.level * std::size(histogram) / 256; >> + for (size_t i = 1; i < blackLevelHistIdx; i++) > ^^^^^^ > This type should be used already at the place this loop was introduced. Oops. > >> + histogram[0] += std::exchange(histogram[i], 0); >> + >> + Histogram yHist(histogram); >> + >> + agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {{ >> + .traits = AgcTraits(yHist), >> + .yHist = yHist, >> + .exposure = frameContext.sensor.exposure, >> + .gain = frameContext.sensor.gain, >> + }}, metadata); >> + } else { >> + agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {}, metadata); >> } >> - >> - auto histogram = stats->yHistogram; >> - const unsigned int blackLevelHistIdx = >> - context.activeState.blc.level * std::size(histogram) / 256; >> - >> - for (unsigned int i = 1; i < blackLevelHistIdx; i++) >> - histogram[0] += std::exchange(histogram[i], 0); >> - >> - const auto &newEv = agc_.calculateNewEv({ >> - .yHist = { histogram }, >> - .exposure = frameContext.sensor.exposure, >> - .gain = frameContext.sensor.gain, >> - }); >> - >> - frameContext.agc.exposure = newEv.exposure; >> - frameContext.agc.gain = newEv.analogueGain; >> - >> - context.activeState.agc.exposure = frameContext.agc.exposure; >> - context.activeState.agc.again = frameContext.agc.gain; >> } >> >> REGISTER_IPA_ALGORITHM(Agc, "Agc") > [...]
diff --git a/src/ipa/libipa/agc.cpp b/src/ipa/libipa/agc.cpp index bb6891cf51..169a53f2cd 100644 --- a/src/ipa/libipa/agc.cpp +++ b/src/ipa/libipa/agc.cpp @@ -707,14 +707,12 @@ void AgcAlgorithm::process(const agc::Session &session, agc::ActiveState &state, metadata.set(controls::ExposureTime, utils::Duration(lineDuration * frameContext.exposure).get<std::micro>()); metadata.set(controls::FrameDuration, frameContext.frameDuration.get<std::micro>()); - metadata.set(controls::ExposureTimeMode, - frameContext.autoExposureEnabled - ? controls::ExposureTimeModeAuto - : controls::ExposureTimeModeManual); - metadata.set(controls::AnalogueGainMode, - frameContext.autoGainEnabled - ? controls::AnalogueGainModeAuto - : controls::AnalogueGainModeManual); + metadata.set(controls::ExposureTimeMode, frameContext.autoExposureEnabled + ? controls::ExposureTimeModeAuto + : controls::ExposureTimeModeManual); + metadata.set(controls::AnalogueGainMode, frameContext.autoGainEnabled + ? controls::AnalogueGainModeAuto + : controls::AnalogueGainModeManual); metadata.set(controls::AeExposureMode, frameContext.exposureMode); metadata.set(controls::AeConstraintMode, frameContext.constraintMode); diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index 7b980c7382..9926b3c593 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -13,30 +13,76 @@ #include <libipa/histogram.h> -#include "control_ids.h" - namespace libcamera { LOG_DEFINE_CATEGORY(IPASoftExposure) namespace ipa::soft::algorithms { +namespace { + +class AgcTraits : public AgcMeanLuminance::Traits +{ +public: + AgcTraits(const Histogram &yHist) + : yHist_(yHist) + { + } + + double estimateLuminance(double gain) const override + { + /* + * TODO: Improve by asking the weight of saturating and non-saturating + * bins directly from the histogram + */ + double sum = 0; + + for (size_t i = 0; i < yHist_.bins(); i++) + sum += std::min<double>(yHist_.bins(), i * gain) * yHist_[i]; + + return sum / yHist_.total() / yHist_.bins(); + } + +private: + const Histogram &yHist_; +}; + +} /* namespace */ + +int Agc::init(IPAContext &context, const ValueNode &tuningData) +{ + int ret = agc_.init(tuningData, context.camHelper.get()); + if (ret) + return ret; + + return agc_.configure(context.configuration.agc, context.activeState.agc, { + .sensorInfo = context.sensorInfo, + .sensorControls = context.sensorControls, + .ctrlMap = context.ctrlMap, + .autoAllowed = true, + }); +} + int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { - agc_.setLimits({ - .exposure = { - context.configuration.agc.exposureMin, - context.configuration.agc.exposureMax, - }, - .gain = { - context.configuration.agc.againMin, - context.configuration.agc.againMax, - }, - .gainMinStep = context.configuration.agc.againMinStep, - .gain1 = context.configuration.agc.again10, + return agc_.configure(context.configuration.agc, context.activeState.agc, { + .sensorInfo = context.sensorInfo, + .sensorControls = context.sensorControls, + .ctrlMap = context.ctrlMap, + .autoAllowed = true, }); +} + +void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, const ControlList &controls) +{ + agc_.queueRequest(context.configuration.agc, context.activeState.agc, frameContext.agc, controls); +} - return 0; +void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) +{ + agc_.prepare(context.configuration.agc, context.activeState.agc, frameContext.agc); } void Agc::process(IPAContext &context, @@ -45,49 +91,25 @@ void Agc::process(IPAContext &context, const SwIspStats *stats, ControlList &metadata) { - utils::Duration exposureTime = - context.configuration.agc.lineDuration * frameContext.sensor.exposure; - metadata.set(controls::ExposureTime, exposureTime.get<std::micro>()); - metadata.set(controls::AnalogueGain, frameContext.sensor.gain); - - if (!context.activeState.agc.valid) { - /* - * Init active-state from sensor values in case updateExposure() - * does not run for the first frame. - */ - context.activeState.agc.exposure = frameContext.sensor.exposure; - context.activeState.agc.again = frameContext.sensor.gain; - context.activeState.agc.valid = true; - } - - if (!stats->valid) { - /* - * Use the new exposure and gain values calculated the last time - * there were valid stats. - */ - frameContext.agc.exposure = context.activeState.agc.exposure; - frameContext.agc.gain = context.activeState.agc.again; - return; + if (stats->valid) { + auto histogram = stats->yHistogram; + + const unsigned int blackLevelHistIdx = + context.activeState.blc.level * std::size(histogram) / 256; + for (size_t i = 1; i < blackLevelHistIdx; i++) + histogram[0] += std::exchange(histogram[i], 0); + + Histogram yHist(histogram); + + agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {{ + .traits = AgcTraits(yHist), + .yHist = yHist, + .exposure = frameContext.sensor.exposure, + .gain = frameContext.sensor.gain, + }}, metadata); + } else { + agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {}, metadata); } - - auto histogram = stats->yHistogram; - const unsigned int blackLevelHistIdx = - context.activeState.blc.level * std::size(histogram) / 256; - - for (unsigned int i = 1; i < blackLevelHistIdx; i++) - histogram[0] += std::exchange(histogram[i], 0); - - const auto &newEv = agc_.calculateNewEv({ - .yHist = { histogram }, - .exposure = frameContext.sensor.exposure, - .gain = frameContext.sensor.gain, - }); - - frameContext.agc.exposure = newEv.exposure; - frameContext.agc.gain = newEv.analogueGain; - - context.activeState.agc.exposure = frameContext.agc.exposure; - context.activeState.agc.again = frameContext.agc.gain; } REGISTER_IPA_ALGORITHM(Agc, "Agc") diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h index 2e156e135c..6e37ce6eff 100644 --- a/src/ipa/simple/algorithms/agc.h +++ b/src/ipa/simple/algorithms/agc.h @@ -9,7 +9,7 @@ #include "algorithm.h" -#include <libipa/agc_msv.h> +#include <libipa/agc.h> namespace libcamera { @@ -18,15 +18,23 @@ namespace ipa::soft::algorithms { class Agc : public Algorithm { public: + int init(IPAContext &context, const ValueNode &tuningData) override; + int configure(IPAContext &context, const IPAConfigInfo &configInfo) override; + void queueRequest(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, const ControlList &controls) override; + + void prepare(IPAContext &context, const uint32_t frame, + IPAFrameContext &frameContext, DebayerParams *params) override; + void process(IPAContext &context, const uint32_t frame, IPAFrameContext &frameContext, const SwIspStats *stats, ControlList &metadata) override; private: - AgcMSV agc_; + AgcAlgorithm agc_; }; } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 96b3c5b21d..63889cd8f6 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -17,6 +17,7 @@ #include "libcamera/internal/matrix.h" #include "libcamera/internal/vector.h" +#include <libipa/agc.h> #include <libipa/camera_sensor_helper.h> #include <libipa/fc_queue.h> @@ -27,10 +28,7 @@ namespace libcamera { namespace ipa::soft { struct IPASessionConfiguration { - struct { - uint32_t exposureMin, exposureMax; - double againMin, againMax, again10, againMinStep; - utils::Duration lineDuration; + struct Agc : agc::Session { } agc; struct { std::optional<uint8_t> level; @@ -38,10 +36,7 @@ struct IPASessionConfiguration { }; struct IPAActiveState { - struct { - uint32_t exposure; - double again; - bool valid; + struct Agc : agc::ActiveState { } agc; struct { @@ -68,9 +63,7 @@ struct IPAActiveState { struct IPAFrameContext : public FrameContext { Matrix<float, 3, 3> ccm; - struct { - uint32_t exposure; - double gain; + struct Agc : agc::FrameContext { } agc; struct { diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 8b5df884d4..f35e269deb 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -107,6 +107,7 @@ int IPASoftSimple::init(const IPASettings &settings, } context_.sensorInfo = sensorInfo; + context_.sensorControls = sensorControls; /* Load the tuning data file */ File file(settings.configurationFile); @@ -180,22 +181,6 @@ int IPASoftSimple::init(const IPASettings &settings, ControlInfoMap::Map ctrlMap = context_.ctrlMap; *ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls); - /* - * Check if the sensor driver supports the controls required by the - * Soft IPA. - * Don't save the min and max control values yet, as e.g. the limits - * for V4L2_CID_EXPOSURE depend on the configured sensor resolution. - */ - if (sensorControls.find(V4L2_CID_EXPOSURE) == sensorControls.end()) { - LOG(IPASoft, Error) << "Don't have exposure control"; - return -EINVAL; - } - - if (sensorControls.find(V4L2_CID_ANALOGUE_GAIN) == sensorControls.end()) { - LOG(IPASoft, Error) << "Don't have gain control"; - return -EINVAL; - } - return 0; } @@ -203,55 +188,17 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo, ControlInfoMap *ip { context_.sensorControls = configInfo.sensorControls; - const ControlInfo &exposureInfo = context_.sensorControls.find(V4L2_CID_EXPOSURE)->second; - const ControlInfo &gainInfo = context_.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; - /* Clear the IPA context before the streaming session. */ context_.configuration = {}; context_.activeState = {}; context_.frameContexts.clear(); - context_.configuration.agc.lineDuration = - context_.sensorInfo.minLineLength * 1.0s / context_.sensorInfo.pixelRate; - context_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>(); - context_.configuration.agc.exposureMax = exposureInfo.max().get<int32_t>(); - if (!context_.configuration.agc.exposureMin) { - LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear"; - context_.configuration.agc.exposureMin = 1; - } - - int32_t againMin = gainInfo.min().get<int32_t>(); - int32_t againMax = gainInfo.max().get<int32_t>(); - int32_t againDef = gainInfo.def().get<int32_t>(); - - if (context_.camHelper) { - context_.configuration.agc.againMin = context_.camHelper->gain(againMin); - context_.configuration.agc.againMax = context_.camHelper->gain(againMax); - context_.configuration.agc.again10 = std::max(context_.configuration.agc.againMin, 1.0); - context_.configuration.agc.againMinStep = - (context_.configuration.agc.againMax - - context_.configuration.agc.againMin) / - 100.0; - } else { - context_.configuration.agc.againMax = againMax; - context_.configuration.agc.again10 = againDef; - context_.configuration.agc.againMin = againMin; - context_.configuration.agc.againMinStep = 1.0; - } - for (const auto &algo : algorithms()) { int ret = algo->configure(context_, configInfo); if (ret) return ret; } - LOG(IPASoft, Info) - << "Exposure " << context_.configuration.agc.exposureMin << "-" - << context_.configuration.agc.exposureMax - << ", gain " << context_.configuration.agc.againMin << "-" - << context_.configuration.agc.againMax - << " (" << context_.configuration.agc.againMinStep << ")"; - *ipaControls = { ControlInfoMap::Map(context_.ctrlMap), controls::controls }; return 0;
Use the `AgcAlgorithm` class to implement the agc algorithm. Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> --- src/ipa/libipa/agc.cpp | 14 ++-- src/ipa/simple/algorithms/agc.cpp | 134 +++++++++++++++++------------- src/ipa/simple/algorithms/agc.h | 12 ++- src/ipa/simple/ipa_context.h | 15 +--- src/ipa/simple/soft_simple.cpp | 55 +----------- 5 files changed, 99 insertions(+), 131 deletions(-)