[libcamera-devel] [PATCH v3 5/5] ipa: rpi: agc: Use channel constraints in the AGC algorithm
Jacopo Mondi
jacopo.mondi at ideasonboard.com
Tue Sep 12 17:21:37 CEST 2023
On Tue, Sep 12, 2023 at 11:24:42AM +0100, David Plowman via libcamera-devel wrote:
> Whenever we run Agc::process(), we store the most recent total
> exposure requested for each channel.
>
> With these values we can apply the channel constraints after
> time-filtering the requested total exposure, but before working out
> how much digital gain is needed.
>
> Signed-off-by: David Plowman <david.plowman at raspberrypi.com>
> Reviewed-by: Naushir Patuck <naush at raspberrypi.com>
> ---
> src/ipa/rpi/controller/rpi/agc.cpp | 22 ++++++--
> src/ipa/rpi/controller/rpi/agc.h | 1 +
> src/ipa/rpi/controller/rpi/agc_channel.cpp | 58 +++++++++++++++++++---
> src/ipa/rpi/controller/rpi/agc_channel.h | 8 ++-
> 4 files changed, 75 insertions(+), 14 deletions(-)
>
> diff --git a/src/ipa/rpi/controller/rpi/agc.cpp b/src/ipa/rpi/controller/rpi/agc.cpp
> index 58ba8839..170432c9 100644
> --- a/src/ipa/rpi/controller/rpi/agc.cpp
> +++ b/src/ipa/rpi/controller/rpi/agc.cpp
> @@ -40,6 +40,7 @@ int Agc::read(const libcamera::YamlObject ¶ms)
> */
> if (!params.contains("channels")) {
> LOG(RPiAgc, Debug) << "Single channel only";
> + channelTotalExposures_.resize(1, 0s);
> channelData_.emplace_back();
> return channelData_.back().channel.read(params, getHardwareConfig());
> }
> @@ -59,6 +60,8 @@ int Agc::read(const libcamera::YamlObject ¶ms)
> return -1;
> }
>
> + channelTotalExposures_.resize(channelData_.size(), 0s);
> +
> return 0;
> }
>
> @@ -236,15 +239,21 @@ static void getChannelIndex(Metadata *metadata, const char *message, unsigned in
> LOG(RPiAgc, Debug) << message;
> }
>
> -static void setChannelIndex(Metadata *metadata, const char *message, unsigned int channelIndex)
> +static libcamera::utils::Duration
> +setChannelIndexGetExposure(Metadata *metadata, const char *message, unsigned int channelIndex)
> {
> std::unique_lock<RPiController::Metadata> lock(*metadata);
> AgcStatus *status = metadata->getLocked<AgcStatus>("agc.status");
> - if (status)
> + libcamera::utils::Duration dur = 0s;
> +
> + if (status) {
> status->channel = channelIndex;
> - else
> + dur = status->totalExposureValue;
> + } else
> /* This does happen at startup, otherwise it would be a Warning or Error. */
> LOG(RPiAgc, Debug) << message;
> +
> + return dur;
> }
>
> void Agc::prepare(Metadata *imageMetadata)
> @@ -308,8 +317,11 @@ void Agc::process(StatisticsPtr &stats, Metadata *imageMetadata)
> /* Can also happen when new channels start. */
> LOG(RPiAgc, Debug) << "process: channel " << channelIndex << " not seen yet";
>
> - channelData.channel.process(stats, &deviceStatus, imageMetadata);
> - setChannelIndex(imageMetadata, "process: no AGC status found", channelIndex);
> + channelData.channel.process(stats, &deviceStatus, imageMetadata, channelTotalExposures_);
> + auto dur = setChannelIndexGetExposure(imageMetadata, "process: no AGC status found",
> + channelIndex);
> + if (dur)
> + channelTotalExposures_[channelIndex] = dur;
>
> /* And onto the next channel for the next call. */
> index_ = (index_ + 1) % activeChannels_.size();
> diff --git a/src/ipa/rpi/controller/rpi/agc.h b/src/ipa/rpi/controller/rpi/agc.h
> index ee85c693..90890439 100644
> --- a/src/ipa/rpi/controller/rpi/agc.h
> +++ b/src/ipa/rpi/controller/rpi/agc.h
> @@ -55,6 +55,7 @@ private:
> std::vector<AgcChannelData> channelData_;
> std::vector<unsigned int> activeChannels_;
> unsigned int index_; /* index into the activeChannels_ */
> + AgcChannelTotalExposures channelTotalExposures_;
> };
>
> } /* namespace RPiController */
> diff --git a/src/ipa/rpi/controller/rpi/agc_channel.cpp b/src/ipa/rpi/controller/rpi/agc_channel.cpp
> index 7fce35b0..4669e8da 100644
> --- a/src/ipa/rpi/controller/rpi/agc_channel.cpp
> +++ b/src/ipa/rpi/controller/rpi/agc_channel.cpp
> @@ -493,7 +493,9 @@ void AgcChannel::prepare(Metadata *imageMetadata)
> }
> }
>
> -void AgcChannel::process(StatisticsPtr &stats, const DeviceStatus *deviceStatus, Metadata *imageMetadata)
> +void AgcChannel::process(StatisticsPtr &stats, const DeviceStatus *deviceStatus,
> + Metadata *imageMetadata,
> + const AgcChannelTotalExposures &channelTotalExposures)
> {
> frameCount_++;
> /*
> @@ -512,12 +514,17 @@ void AgcChannel::process(StatisticsPtr &stats, const DeviceStatus *deviceStatus,
> computeTargetExposure(gain);
> /* The results have to be filtered so as not to change too rapidly. */
> filterExposure();
> + /*
> + * We may be asked to limit the exposure using other channels. If another channel
> + * determines our upper bound we may want to know this later.
> + */
> + bool channelBound = applyChannelConstraints(channelTotalExposures);
> /*
> * Some of the exposure has to be applied as digital gain, so work out
> - * what that is. This function also tells us whether it's decided to
> - * "desaturate" the image more quickly.
> + * what that is. It also tells us whether it's trying to desaturate the image
> + * more quickly, which can only happen when another channel is not limiting us.
> */
> - bool desaturate = applyDigitalGain(gain, targetY);
> + bool desaturate = applyDigitalGain(gain, targetY, channelBound);
> /*
> * The last thing is to divide up the exposure value into a shutter time
> * and analogue gain, according to the current exposure mode.
> @@ -796,7 +803,44 @@ void AgcChannel::computeTargetExposure(double gain)
> LOG(RPiAgc, Debug) << "Target totalExposure " << target_.totalExposure;
> }
>
> -bool AgcChannel::applyDigitalGain(double gain, double targetY)
> +bool AgcChannel::applyChannelConstraints(const AgcChannelTotalExposures &channelTotalExposures)
> +{
> + bool channelBound = false;
> + LOG(RPiAgc, Debug)
> + << "Total exposure before channel constraints " << filtered_.totalExposure;
> +
> + for (const auto &constraint : config_.channelConstraints) {
> + LOG(RPiAgc, Debug)
> + << "Check constraint: channel " << constraint.channel << " bound "
> + << (constraint.bound == AgcChannelConstraint::Bound::UPPER ? "UPPER" : "LOWER")
> + << " factor " << constraint.factor;
> + if (constraint.channel >= channelTotalExposures.size() ||
> + !channelTotalExposures[constraint.channel]) {
> + LOG(RPiAgc, Debug) << "no such channel or no exposure available- skipped";
> + continue;
> + }
> +
> + libcamera::utils::Duration limitExposure =
> + channelTotalExposures[constraint.channel] * constraint.factor;
I'm still having troubles visualizing how this gets specified in a config file,
specifically what guarantees the interdepencies between channels are
correct. But that's likely not a problem for now.
Code-wise this is fine
Reviewed-by: Jacopo Mondi <jacopo.mondi at ideasonboard.com>
Thanks
j
> + LOG(RPiAgc, Debug) << "Limit exposure " << limitExposure;
> + if ((constraint.bound == AgcChannelConstraint::Bound::UPPER &&
> + filtered_.totalExposure > limitExposure) ||
> + (constraint.bound == AgcChannelConstraint::Bound::LOWER &&
> + filtered_.totalExposure < limitExposure)) {
> + filtered_.totalExposure = limitExposure;
> + LOG(RPiAgc, Debug) << "Constraint applies";
> + channelBound = true;
> + } else
> + LOG(RPiAgc, Debug) << "Constraint does not apply";
> + }
> +
> + LOG(RPiAgc, Debug)
> + << "Total exposure after channel constraints " << filtered_.totalExposure;
> +
> + return channelBound;
> +}
> +
> +bool AgcChannel::applyDigitalGain(double gain, double targetY, bool channelBound)
> {
> double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 });
> ASSERT(minColourGain != 0.0);
> @@ -816,8 +860,8 @@ bool AgcChannel::applyDigitalGain(double gain, double targetY)
> * quickly (and we then approach the correct value more quickly from
> * below).
> */
> - bool desaturate = targetY > config_.fastReduceThreshold &&
> - gain < sqrt(targetY);
> + bool desaturate = !channelBound &&
> + targetY > config_.fastReduceThreshold && gain < sqrt(targetY);
> if (desaturate)
> dg /= config_.fastReduceThreshold;
> LOG(RPiAgc, Debug) << "Digital gain " << dg << " desaturate? " << desaturate;
> diff --git a/src/ipa/rpi/controller/rpi/agc_channel.h b/src/ipa/rpi/controller/rpi/agc_channel.h
> index 5d2d8a11..1745f2ef 100644
> --- a/src/ipa/rpi/controller/rpi/agc_channel.h
> +++ b/src/ipa/rpi/controller/rpi/agc_channel.h
> @@ -21,6 +21,8 @@
>
> namespace RPiController {
>
> +using AgcChannelTotalExposures = std::vector<libcamera::utils::Duration>;
> +
> struct AgcMeteringMode {
> std::vector<double> weights;
> int read(const libcamera::YamlObject ¶ms);
> @@ -95,7 +97,8 @@ public:
> void disableAuto();
> void switchMode(CameraMode const &cameraMode, Metadata *metadata);
> void prepare(Metadata *imageMetadata);
> - void process(StatisticsPtr &stats, const DeviceStatus *deviceStatus, Metadata *imageMetadata);
> + void process(StatisticsPtr &stats, const DeviceStatus *deviceStatus, Metadata *imageMetadata,
> + const AgcChannelTotalExposures &channelTotalExposures);
>
> private:
> bool updateLockStatus(DeviceStatus const &deviceStatus);
> @@ -107,7 +110,8 @@ private:
> double &gain, double &targetY);
> void computeTargetExposure(double gain);
> void filterExposure();
> - bool applyDigitalGain(double gain, double targetY);
> + bool applyChannelConstraints(const AgcChannelTotalExposures &channelTotalExposures);
> + bool applyDigitalGain(double gain, double targetY, bool channelBound);
> void divideUpExposure();
> void writeAndFinish(Metadata *imageMetadata, bool desaturate);
> libcamera::utils::Duration limitShutter(libcamera::utils::Duration shutter);
> --
> 2.30.2
>
More information about the libcamera-devel
mailing list