Message ID | 20240607080330.2667994-3-paul.elder@ideasonboard.com |
---|---|
State | New |
Headers | show |
Series |
|
Related | show |
Hi Paul, Thank you for the patch. On Fri, Jun 07, 2024 at 05:03:29PM +0900, Paul Elder wrote: > Change the relative luminance target from a scalar value to a piecewise > linear function that needs to be sampled by the estimate lux value. One thing I would like to capture somewhere is the rationale for this. Why should the target luminance (essentially the average Y value across the image if we simplify it) depend on how bright the scene is ? I looked at the RPi tuning files, and they push the target luminance slightly up the brighter the scene is. Is this because we want to image to appear brighter to a human eye when we know that the scene is brigher ? > Also change the rkisp1 and ipu3 IPAs accordingly, as they use the libipa > agc. As they both don't yet have lux modules, hardcode them to a single > lux value for now. > > This affects the format of the tuning files, but as there aren't yet any > this shouldn't be an issue. Sounds fine, but I would still like to see documentation of the format :-) Or, as a first step, at least a sample tuning file in-tree. > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> > Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> > Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com> > > --- > Changes in v5: > - use new PointF that's a typedef of Vector > > Changes in v4: > - construct default pwl with *two* points so that it succeeds > > No change in v3 > > Changes in v2: > - s/FPoint/PointF/ > - add warning when using default relative luminance target when loading > from the tuning file fails > --- > src/ipa/ipu3/algorithms/agc.cpp | 5 ++++- > src/ipa/libipa/agc_mean_luminance.cpp | 32 +++++++++++++++++++++------ > src/ipa/libipa/agc_mean_luminance.h | 7 +++--- > src/ipa/rkisp1/algorithms/agc.cpp | 5 ++++- > 4 files changed, 37 insertions(+), 12 deletions(-) > > diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp > index 0e0114f6d..7f71bec06 100644 > --- a/src/ipa/ipu3/algorithms/agc.cpp > +++ b/src/ipa/ipu3/algorithms/agc.cpp > @@ -222,12 +222,15 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, > double analogueGain = frameContext.sensor.gain; > utils::Duration effectiveExposureValue = exposureTime * analogueGain; > > + /* \todo Plumb in the lux value. Requires a lux algo + tuning */ > + double lux = 400; > + > utils::Duration shutterTime; > double aGain, dGain; > std::tie(shutterTime, aGain, dGain) = > calculateNewEv(context.activeState.agc.constraintMode, > context.activeState.agc.exposureMode, hist, > - effectiveExposureValue); > + effectiveExposureValue, lux); > > LOG(IPU3Agc, Debug) > << "Divided up shutter, analogue gain and digital gain are " > diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp > index 271b5ae4b..10c16850d 100644 > --- a/src/ipa/libipa/agc_mean_luminance.cpp > +++ b/src/ipa/libipa/agc_mean_luminance.cpp > @@ -134,7 +134,7 @@ static constexpr double kDefaultRelativeLuminanceTarget = 0.16; > */ > > AgcMeanLuminance::AgcMeanLuminance() > - : frameCount_(0), filteredExposure_(0s), relativeLuminanceTarget_(0) > + : frameCount_(0), filteredExposure_(0s) > { > } > > @@ -142,8 +142,17 @@ AgcMeanLuminance::~AgcMeanLuminance() = default; > > void AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) > { > - relativeLuminanceTarget_ = > - tuningData["relativeLuminanceTarget"].get<double>(kDefaultRelativeLuminanceTarget); > + int ret = relativeLuminanceTarget_.readYaml(tuningData["relativeLuminanceTarget"]); > + if (ret == 0) > + return; > + > + LOG(AgcMeanLuminance, Warning) > + << "Failed to load tuning parameter 'relativeLuminanceTarget', " > + << "using default [0, " << kDefaultRelativeLuminanceTarget << "]"; > + > + std::vector<Pwl::PointF> points = { Pwl::PointF({ 0, kDefaultRelativeLuminanceTarget }), > + Pwl::PointF({ 10000, kDefaultRelativeLuminanceTarget }) }; > + relativeLuminanceTarget_ = Pwl(points); > } > > void AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) > @@ -421,11 +430,18 @@ void AgcMeanLuminance::setLimits(utils::Duration minShutter, > /** > * \brief Estimate the initial gain needed to achieve a relative luminance > * target > + * \param[in] lux The lux value at which to sample the luminance target pwl > + * > + * To account for non-linearity caused by saturation, the value needs to be > + * estimated in an iterative process, as multiplying by a gain will not increase > + * the relative luminance by the same factor if some image regions are saturated s/$/./ I would *really* like checkstyle to catch this, but parsing documentation isn't easy :-S This being said, the same comment is already present inside the function. Is there a reason to duplicate it ? > + * > * \return The calculated initial gain > */ > -double AgcMeanLuminance::estimateInitialGain() const > +double AgcMeanLuminance::estimateInitialGain(double lux) const > { > - double yTarget = relativeLuminanceTarget_; > + double yTarget = > + relativeLuminanceTarget_.eval(relativeLuminanceTarget_.domain().clamp(lux)); > double yGain = 1.0; > > /* > @@ -520,6 +536,7 @@ utils::Duration AgcMeanLuminance::filterExposure(utils::Duration exposureValue) > * the calculated gain > * \param[in] effectiveExposureValue The EV applied to the frame from which the > * statistics in use derive > + * \param[in] lux The lux value at which to sample the luminance target pwl > * > * Calculate a new exposure value to try to obtain the target. The calculated > * exposure value is filtered to prevent rapid changes from frame to frame, and > @@ -531,7 +548,8 @@ std::tuple<utils::Duration, double, double> > AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex, > uint32_t exposureModeIndex, > const Histogram &yHist, > - utils::Duration effectiveExposureValue) > + utils::Duration effectiveExposureValue, > + double lux) > { > /* > * The pipeline handler should validate that we have received an allowed > @@ -540,7 +558,7 @@ AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex, > std::shared_ptr<ExposureModeHelper> exposureModeHelper = > exposureModeHelpers_.at(exposureModeIndex); > > - double gain = estimateInitialGain(); > + double gain = estimateInitialGain(lux); > gain = constraintClampGain(constraintModeIndex, yHist, gain); > > /* > diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h > index 0a81c6d28..6ec2a0dc9 100644 > --- a/src/ipa/libipa/agc_mean_luminance.h > +++ b/src/ipa/libipa/agc_mean_luminance.h > @@ -18,6 +18,7 @@ > > #include "exposure_mode_helper.h" > #include "histogram.h" > +#include "pwl.h" > > namespace libcamera { > > @@ -62,7 +63,7 @@ public: > > std::tuple<utils::Duration, double, double> > calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex, > - const Histogram &yHist, utils::Duration effectiveExposureValue); > + const Histogram &yHist, utils::Duration effectiveExposureValue, double lux); > > void resetFrameCount() > { > @@ -76,7 +77,7 @@ private: > void parseConstraint(const YamlObject &modeDict, int32_t id); > int parseConstraintModes(const YamlObject &tuningData); > int parseExposureModes(const YamlObject &tuningData); > - double estimateInitialGain() const; > + double estimateInitialGain(double lux) const; > double constraintClampGain(uint32_t constraintModeIndex, > const Histogram &hist, > double gain); > @@ -84,7 +85,7 @@ private: > > uint64_t frameCount_; > utils::Duration filteredExposure_; > - double relativeLuminanceTarget_; > + Pwl relativeLuminanceTarget_; > > std::map<int32_t, std::vector<AgcConstraint>> constraintModes_; > std::map<int32_t, std::shared_ptr<ExposureModeHelper>> exposureModeHelpers_; > diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp > index 3bbafd978..e9f1f2095 100644 > --- a/src/ipa/rkisp1/algorithms/agc.cpp > +++ b/src/ipa/rkisp1/algorithms/agc.cpp > @@ -379,12 +379,15 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, > double analogueGain = frameContext.sensor.gain; > utils::Duration effectiveExposureValue = exposureTime * analogueGain; > > + /* \todo Plumb in the lux value. Requires a lux algo + tuning */ > + double lux = 400; > + > utils::Duration shutterTime; > double aGain, dGain; > std::tie(shutterTime, aGain, dGain) = > calculateNewEv(context.activeState.agc.constraintMode, > context.activeState.agc.exposureMode, > - hist, effectiveExposureValue); > + hist, effectiveExposureValue, lux); > > LOG(RkISP1Agc, Debug) > << "Divided up shutter, analogue gain and digital gain are "
diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp index 0e0114f6d..7f71bec06 100644 --- a/src/ipa/ipu3/algorithms/agc.cpp +++ b/src/ipa/ipu3/algorithms/agc.cpp @@ -222,12 +222,15 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, double analogueGain = frameContext.sensor.gain; utils::Duration effectiveExposureValue = exposureTime * analogueGain; + /* \todo Plumb in the lux value. Requires a lux algo + tuning */ + double lux = 400; + utils::Duration shutterTime; double aGain, dGain; std::tie(shutterTime, aGain, dGain) = calculateNewEv(context.activeState.agc.constraintMode, context.activeState.agc.exposureMode, hist, - effectiveExposureValue); + effectiveExposureValue, lux); LOG(IPU3Agc, Debug) << "Divided up shutter, analogue gain and digital gain are " diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp index 271b5ae4b..10c16850d 100644 --- a/src/ipa/libipa/agc_mean_luminance.cpp +++ b/src/ipa/libipa/agc_mean_luminance.cpp @@ -134,7 +134,7 @@ static constexpr double kDefaultRelativeLuminanceTarget = 0.16; */ AgcMeanLuminance::AgcMeanLuminance() - : frameCount_(0), filteredExposure_(0s), relativeLuminanceTarget_(0) + : frameCount_(0), filteredExposure_(0s) { } @@ -142,8 +142,17 @@ AgcMeanLuminance::~AgcMeanLuminance() = default; void AgcMeanLuminance::parseRelativeLuminanceTarget(const YamlObject &tuningData) { - relativeLuminanceTarget_ = - tuningData["relativeLuminanceTarget"].get<double>(kDefaultRelativeLuminanceTarget); + int ret = relativeLuminanceTarget_.readYaml(tuningData["relativeLuminanceTarget"]); + if (ret == 0) + return; + + LOG(AgcMeanLuminance, Warning) + << "Failed to load tuning parameter 'relativeLuminanceTarget', " + << "using default [0, " << kDefaultRelativeLuminanceTarget << "]"; + + std::vector<Pwl::PointF> points = { Pwl::PointF({ 0, kDefaultRelativeLuminanceTarget }), + Pwl::PointF({ 10000, kDefaultRelativeLuminanceTarget }) }; + relativeLuminanceTarget_ = Pwl(points); } void AgcMeanLuminance::parseConstraint(const YamlObject &modeDict, int32_t id) @@ -421,11 +430,18 @@ void AgcMeanLuminance::setLimits(utils::Duration minShutter, /** * \brief Estimate the initial gain needed to achieve a relative luminance * target + * \param[in] lux The lux value at which to sample the luminance target pwl + * + * To account for non-linearity caused by saturation, the value needs to be + * estimated in an iterative process, as multiplying by a gain will not increase + * the relative luminance by the same factor if some image regions are saturated + * * \return The calculated initial gain */ -double AgcMeanLuminance::estimateInitialGain() const +double AgcMeanLuminance::estimateInitialGain(double lux) const { - double yTarget = relativeLuminanceTarget_; + double yTarget = + relativeLuminanceTarget_.eval(relativeLuminanceTarget_.domain().clamp(lux)); double yGain = 1.0; /* @@ -520,6 +536,7 @@ utils::Duration AgcMeanLuminance::filterExposure(utils::Duration exposureValue) * the calculated gain * \param[in] effectiveExposureValue The EV applied to the frame from which the * statistics in use derive + * \param[in] lux The lux value at which to sample the luminance target pwl * * Calculate a new exposure value to try to obtain the target. The calculated * exposure value is filtered to prevent rapid changes from frame to frame, and @@ -531,7 +548,8 @@ std::tuple<utils::Duration, double, double> AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex, const Histogram &yHist, - utils::Duration effectiveExposureValue) + utils::Duration effectiveExposureValue, + double lux) { /* * The pipeline handler should validate that we have received an allowed @@ -540,7 +558,7 @@ AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex, std::shared_ptr<ExposureModeHelper> exposureModeHelper = exposureModeHelpers_.at(exposureModeIndex); - double gain = estimateInitialGain(); + double gain = estimateInitialGain(lux); gain = constraintClampGain(constraintModeIndex, yHist, gain); /* diff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h index 0a81c6d28..6ec2a0dc9 100644 --- a/src/ipa/libipa/agc_mean_luminance.h +++ b/src/ipa/libipa/agc_mean_luminance.h @@ -18,6 +18,7 @@ #include "exposure_mode_helper.h" #include "histogram.h" +#include "pwl.h" namespace libcamera { @@ -62,7 +63,7 @@ public: std::tuple<utils::Duration, double, double> calculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex, - const Histogram &yHist, utils::Duration effectiveExposureValue); + const Histogram &yHist, utils::Duration effectiveExposureValue, double lux); void resetFrameCount() { @@ -76,7 +77,7 @@ private: void parseConstraint(const YamlObject &modeDict, int32_t id); int parseConstraintModes(const YamlObject &tuningData); int parseExposureModes(const YamlObject &tuningData); - double estimateInitialGain() const; + double estimateInitialGain(double lux) const; double constraintClampGain(uint32_t constraintModeIndex, const Histogram &hist, double gain); @@ -84,7 +85,7 @@ private: uint64_t frameCount_; utils::Duration filteredExposure_; - double relativeLuminanceTarget_; + Pwl relativeLuminanceTarget_; std::map<int32_t, std::vector<AgcConstraint>> constraintModes_; std::map<int32_t, std::shared_ptr<ExposureModeHelper>> exposureModeHelpers_; diff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp index 3bbafd978..e9f1f2095 100644 --- a/src/ipa/rkisp1/algorithms/agc.cpp +++ b/src/ipa/rkisp1/algorithms/agc.cpp @@ -379,12 +379,15 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame, double analogueGain = frameContext.sensor.gain; utils::Duration effectiveExposureValue = exposureTime * analogueGain; + /* \todo Plumb in the lux value. Requires a lux algo + tuning */ + double lux = 400; + utils::Duration shutterTime; double aGain, dGain; std::tie(shutterTime, aGain, dGain) = calculateNewEv(context.activeState.agc.constraintMode, context.activeState.agc.exposureMode, - hist, effectiveExposureValue); + hist, effectiveExposureValue, lux); LOG(RkISP1Agc, Debug) << "Divided up shutter, analogue gain and digital gain are "