[libcamera-devel] [PATCH v2 3/5] ipa: rpi: agc: Implementation of multi-channel AGC
Jacopo Mondi
jacopo.mondi at ideasonboard.com
Wed Aug 30 09:56:59 CEST 2023
Hi David
On Wed, Aug 23, 2023 at 01:09:13PM +0100, David Plowman via libcamera-devel wrote:
> The switchMode, prepare and process methods are updated to implement
> multi-channel AGC correctly:
>
> * switchMode now invokes switchMode on all the channels (whether
> active or not).
>
> * prepare must find what channel the current frame is, and run on
> behalf of that channel.
>
> * process updates the most recent DeviceStatus and statistics for the
> channel of the frame that has just arrived, but generates updated
> values working through the active channels in round-robin fashion.
>
> One minor detail in process is that we don't want to change the
> DeviceStatus metadata of the current frame, so we now pass this to the
> AgcChannel's process method, rather than letting it find the
> DeviceStatus in the metadata.
>
> Signed-off-by: David Plowman <david.plowman at raspberrypi.com>
> Reviewed-by: Naushir Patuck <naush at raspberrypi.com>
> ---
> src/ipa/rpi/controller/agc_status.h | 1 +
> src/ipa/rpi/controller/rpi/agc.cpp | 108 +++++++++++++++++++--
> src/ipa/rpi/controller/rpi/agc.h | 1 +
> src/ipa/rpi/controller/rpi/agc_channel.cpp | 13 +--
> src/ipa/rpi/controller/rpi/agc_channel.h | 4 +-
> 5 files changed, 109 insertions(+), 18 deletions(-)
>
> diff --git a/src/ipa/rpi/controller/agc_status.h b/src/ipa/rpi/controller/agc_status.h
> index 597eddd7..e5c4ee22 100644
> --- a/src/ipa/rpi/controller/agc_status.h
> +++ b/src/ipa/rpi/controller/agc_status.h
> @@ -36,6 +36,7 @@ struct AgcStatus {
> int floatingRegionEnable;
> libcamera::utils::Duration fixedShutter;
> double fixedAnalogueGain;
> + unsigned int channel;
> };
>
> struct AgcPrepareStatus {
> diff --git a/src/ipa/rpi/controller/rpi/agc.cpp b/src/ipa/rpi/controller/rpi/agc.cpp
> index c9c9c297..7e627bba 100644
> --- a/src/ipa/rpi/controller/rpi/agc.cpp
> +++ b/src/ipa/rpi/controller/rpi/agc.cpp
> @@ -22,7 +22,8 @@ LOG_DEFINE_CATEGORY(RPiAgc)
>
> Agc::Agc(Controller *controller)
> : AgcAlgorithm(controller),
> - activeChannels_({ 0 })
> + activeChannels_({ 0 }),
> + index_(0)
> {
> }
>
> @@ -203,20 +204,113 @@ void Agc::setActiveChannels(const std::vector<unsigned int> &activeChannels)
> void Agc::switchMode(CameraMode const &cameraMode,
> Metadata *metadata)
> {
> - LOG(RPiAgc, Debug) << "switchMode for channel 0";
> - channelData_[0].channel.switchMode(cameraMode, metadata);
> + /*
> + * We run switchMode on every channel, and then we're going to start over
> + * with the first active channel again which means that this channel's
> + * status needs to be the one we leave in the metadata.
> + */
> + AgcStatus status;
> +
> + for (unsigned int channelIndex = 0; channelIndex < channelData_.size(); channelIndex++) {
> + LOG(RPiAgc, Debug) << "switchMode for channel " << channelIndex;
> + channelData_[channelIndex].channel.switchMode(cameraMode, metadata);
> + if (channelIndex == activeChannels_[0])
> + metadata->get("agc.status", status);
> + }
> +
> + status.channel = activeChannels_[0];
> + metadata->set("agc.status", status);
> + index_ = 0;
> +}
> +
> +static void getChannelIndex(Metadata *metadata, const char *message, unsigned int &channelIndex)
> +{
> + std::unique_lock<RPiController::Metadata> lock(*metadata);
> + AgcStatus *status = metadata->getLocked<AgcStatus>("agc.delayed_status");
> + if (status)
> + channelIndex = status->channel;
I'm missing in the code base where delayed_status.channel gets set :/
> + else
> + /* This does happen at startup, otherwise it would be a Warning or Error. */
> + LOG(RPiAgc, Debug) << message;
If this is a known condition, do you need to have the message
polluting the logs ?
> +}
> +
> +static void setChannelIndex(Metadata *metadata, const char *message, unsigned int channelIndex)
> +{
> + std::unique_lock<RPiController::Metadata> lock(*metadata);
> + AgcStatus *status = metadata->getLocked<AgcStatus>("agc.status");
> + if (status)
> + status->channel = channelIndex;
> + else
> + /* This does happen at startup, otherwise it would be a Warning or Error. */
> + LOG(RPiAgc, Debug) << message;
ditto
> }
>
> void Agc::prepare(Metadata *imageMetadata)
> {
> - LOG(RPiAgc, Debug) << "prepare for channel 0";
> - channelData_[0].channel.prepare(imageMetadata);
> + /*
> + * The DeviceStatus in the metadata should be correct for the image we
> + * are processing. THe delayed status should tell us what channel this frame
> + * was from, so we will use that channel's prepare method.
> + *
> + * \todo To be honest, there's not much that's stateful in the prepare methods
> + * so we should perhaps re-evaluate whether prepare even needs to be done
> + * "per channel".
on which channel would you do 'prepare' if not the last active one ?
> + */
> + unsigned int channelIndex = activeChannels_[0];
> + getChannelIndex(imageMetadata, "prepare: no delayed status", channelIndex);
> +
> + LOG(RPiAgc, Debug) << "prepare for channel " << channelIndex;
> + channelData_[channelIndex].channel.prepare(imageMetadata);
> }
>
> void Agc::process(StatisticsPtr &stats, Metadata *imageMetadata)
> {
> - LOG(RPiAgc, Debug) << "process for channel 0";
> - channelData_[0].channel.process(stats, imageMetadata);
> + /*
> + * We want to generate values for the next channel in round robin fashion
> + * (i.e. the channel at location index_ in the activeChannel list), even though
> + * the statistics we have will be for a different channel (which we find
> + * again from the delayed status).
> + */
> +
> + /* Generate updated AGC values for this channel: */
> + unsigned int channelIndex = activeChannels_[index_];
> + AgcChannelData &channelData = channelData_[channelIndex];
> + /* Stats are from this channel: */
> + unsigned int statsIndex = 0;
why not use 'activeChannels_[index_]' again here ?
> + getChannelIndex(imageMetadata, "process: no delayed status for stats", statsIndex);
> + LOG(RPiAgc, Debug) << "process for channel " << channelIndex;
> +
> + /*
> + * We keep a cache of the most recent DeviceStatus and stats for each channel,
> + * so that we can invoke the next channel's process method with the most up to date
> + * values.
> + */
> + LOG(RPiAgc, Debug) << "Save DeviceStatus and stats for channel " << statsIndex;
> + DeviceStatus deviceStatus;
> + if (imageMetadata->get<DeviceStatus>("device.status", deviceStatus) == 0)
> + channelData_[statsIndex].deviceStatus = deviceStatus;
> + else
> + /* Every frame should have a DeviceStatus. */
> + LOG(RPiAgc, Error) << "process: no device status found";
> + channelData_[statsIndex].statistics = stats;
Again I'm missing why we need to use a different channelIndex and
statsIndex..
> +
> + /*
> + * Finally fetch the most recent DeviceStatus and stats for this channel, if both
> + * exist, and call process(). We must make the agc.status metadata record correctly
> + * which channel this is.
> + */
> + if (channelData.statistics && channelData.deviceStatus) {
> + deviceStatus = *channelData.deviceStatus;
> + stats = channelData.statistics;
Do you need to copy ? Could channelData.statistics get overwritten
during the process() call chain ?
> + } else
> + /* 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);
> +
> + /* And onto the next channel for the next call. */
> + index_ = (index_ + 1) % activeChannels_.size();
> }
>
> /* Register algorithm with the system. */
> diff --git a/src/ipa/rpi/controller/rpi/agc.h b/src/ipa/rpi/controller/rpi/agc.h
> index a9158910..2eed2bab 100644
> --- a/src/ipa/rpi/controller/rpi/agc.h
> +++ b/src/ipa/rpi/controller/rpi/agc.h
> @@ -53,6 +53,7 @@ private:
> int checkChannel(unsigned int channel) const;
> std::vector<AgcChannelData> channelData_;
> std::vector<unsigned int> activeChannels_;
> + unsigned int index_; /* index into the activeChannels_ */
> };
>
> } /* namespace RPiController */
> diff --git a/src/ipa/rpi/controller/rpi/agc_channel.cpp b/src/ipa/rpi/controller/rpi/agc_channel.cpp
> index d6e30ef2..ddec611f 100644
> --- a/src/ipa/rpi/controller/rpi/agc_channel.cpp
> +++ b/src/ipa/rpi/controller/rpi/agc_channel.cpp
> @@ -447,7 +447,7 @@ void AgcChannel::prepare(Metadata *imageMetadata)
> }
> }
>
> -void AgcChannel::process(StatisticsPtr &stats, Metadata *imageMetadata)
> +void AgcChannel::process(StatisticsPtr &stats, const DeviceStatus *deviceStatus, Metadata *imageMetadata)
> {
> frameCount_++;
> /*
> @@ -458,7 +458,7 @@ void AgcChannel::process(StatisticsPtr &stats, Metadata *imageMetadata)
> /* Fetch the AWB status immediately, so that we can assume it's there. */
> fetchAwbStatus(imageMetadata);
> /* Get the current exposure values for the frame that's just arrived. */
> - fetchCurrentExposure(imageMetadata);
> + fetchCurrentExposure(deviceStatus);
> /* Compute the total gain we require relative to the current exposure. */
> double gain, targetY;
> computeGain(stats, imageMetadata, gain, targetY);
> @@ -570,18 +570,13 @@ void AgcChannel::housekeepConfig()
> << meteringModeName_;
> }
>
> -void AgcChannel::fetchCurrentExposure(Metadata *imageMetadata)
> +void AgcChannel::fetchCurrentExposure(const DeviceStatus *deviceStatus)
> {
> - std::unique_lock<Metadata> lock(*imageMetadata);
> - DeviceStatus *deviceStatus =
> - imageMetadata->getLocked<DeviceStatus>("device.status");
> if (!deviceStatus)
> LOG(RPiAgc, Fatal) << "No device metadata";
> current_.shutter = deviceStatus->shutterSpeed;
> current_.analogueGain = deviceStatus->analogueGain;
> - AgcStatus *agcStatus =
> - imageMetadata->getLocked<AgcStatus>("agc.status");
> - current_.totalExposure = agcStatus ? agcStatus->totalExposureValue : 0s;
> + current_.totalExposure = 0s; /* this value is unused */
Uh, why is this not used anymore ? :)
> current_.totalExposureNoDG = current_.shutter * current_.analogueGain;
> }
>
> diff --git a/src/ipa/rpi/controller/rpi/agc_channel.h b/src/ipa/rpi/controller/rpi/agc_channel.h
> index dc4356f3..0e3d44b0 100644
> --- a/src/ipa/rpi/controller/rpi/agc_channel.h
> +++ b/src/ipa/rpi/controller/rpi/agc_channel.h
> @@ -83,13 +83,13 @@ public:
> void disableAuto();
> void switchMode(CameraMode const &cameraMode, Metadata *metadata);
> void prepare(Metadata *imageMetadata);
> - void process(StatisticsPtr &stats, Metadata *imageMetadata);
> + void process(StatisticsPtr &stats, const DeviceStatus *deviceStatus, Metadata *imageMetadata);
>
> private:
> bool updateLockStatus(DeviceStatus const &deviceStatus);
> AgcConfig config_;
> void housekeepConfig();
> - void fetchCurrentExposure(Metadata *imageMetadata);
> + void fetchCurrentExposure(const DeviceStatus *deviceStatus);
> void fetchAwbStatus(Metadata *imageMetadata);
> void computeGain(StatisticsPtr &statistics, Metadata *imageMetadata,
> double &gain, double &targetY);
> --
> 2.30.2
>
More information about the libcamera-devel
mailing list