ipa: rpi: agc: Use channel constraints in the AGC algorithm

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@raspberrypi.com>
Reviewed-by: Naushir Patuck <naush@raspberrypi.com>
Reviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>
This commit is contained in:
David Plowman 2023-09-15 16:58:44 +01:00 committed by Jacopo Mondi
parent 7927c44735
commit 7127954aaa
4 changed files with 76 additions and 15 deletions

View file

@ -39,6 +39,7 @@ int Agc::read(const libcamera::YamlObject &params)
*/ */
if (!params.contains("channels")) { if (!params.contains("channels")) {
LOG(RPiAgc, Debug) << "Single channel only"; LOG(RPiAgc, Debug) << "Single channel only";
channelTotalExposures_.resize(1, 0s);
channelData_.emplace_back(); channelData_.emplace_back();
return channelData_.back().channel.read(params, getHardwareConfig()); return channelData_.back().channel.read(params, getHardwareConfig());
} }
@ -58,6 +59,8 @@ int Agc::read(const libcamera::YamlObject &params)
return -1; return -1;
} }
channelTotalExposures_.resize(channelData_.size(), 0s);
return 0; return 0;
} }
@ -236,16 +239,22 @@ static void getDelayedChannelIndex(Metadata *metadata, const char *message, unsi
} }
} }
static void setCurrentChannelIndex(Metadata *metadata, const char *message, unsigned int channelIndex) static libcamera::utils::Duration
setCurrentChannelIndexGetExposure(Metadata *metadata, const char *message, unsigned int channelIndex)
{ {
std::unique_lock<RPiController::Metadata> lock(*metadata); std::unique_lock<RPiController::Metadata> lock(*metadata);
AgcStatus *status = metadata->getLocked<AgcStatus>("agc.status"); AgcStatus *status = metadata->getLocked<AgcStatus>("agc.status");
if (status) libcamera::utils::Duration dur = 0s;
if (status) {
status->channel = channelIndex; status->channel = channelIndex;
else { dur = status->totalExposureValue;
} else {
/* This does happen at startup, otherwise it would be a Warning or Error. */ /* This does happen at startup, otherwise it would be a Warning or Error. */
LOG(RPiAgc, Debug) << message; LOG(RPiAgc, Debug) << message;
} }
return dur;
} }
void Agc::prepare(Metadata *imageMetadata) void Agc::prepare(Metadata *imageMetadata)
@ -310,8 +319,11 @@ void Agc::process(StatisticsPtr &stats, Metadata *imageMetadata)
LOG(RPiAgc, Debug) << "process: channel " << channelIndex << " not seen yet"; LOG(RPiAgc, Debug) << "process: channel " << channelIndex << " not seen yet";
} }
channelData.channel.process(stats, deviceStatus, imageMetadata); channelData.channel.process(stats, deviceStatus, imageMetadata, channelTotalExposures_);
setCurrentChannelIndex(imageMetadata, "process: no AGC status found", channelIndex); auto dur = setCurrentChannelIndexGetExposure(imageMetadata, "process: no AGC status found",
channelIndex);
if (dur)
channelTotalExposures_[channelIndex] = dur;
/* And onto the next channel for the next call. */ /* And onto the next channel for the next call. */
index_ = (index_ + 1) % activeChannels_.size(); index_ = (index_ + 1) % activeChannels_.size();

View file

@ -55,6 +55,7 @@ private:
std::vector<AgcChannelData> channelData_; std::vector<AgcChannelData> channelData_;
std::vector<unsigned int> activeChannels_; std::vector<unsigned int> activeChannels_;
unsigned int index_; /* index into the activeChannels_ */ unsigned int index_; /* index into the activeChannels_ */
AgcChannelTotalExposures channelTotalExposures_;
}; };
} /* namespace RPiController */ } /* namespace RPiController */

View file

@ -493,7 +493,9 @@ void AgcChannel::prepare(Metadata *imageMetadata)
} }
} }
void AgcChannel::process(StatisticsPtr &stats, DeviceStatus const &deviceStatus, Metadata *imageMetadata) void AgcChannel::process(StatisticsPtr &stats, DeviceStatus const &deviceStatus,
Metadata *imageMetadata,
const AgcChannelTotalExposures &channelTotalExposures)
{ {
frameCount_++; frameCount_++;
/* /*
@ -513,11 +515,16 @@ void AgcChannel::process(StatisticsPtr &stats, DeviceStatus const &deviceStatus,
/* The results have to be filtered so as not to change too rapidly. */ /* The results have to be filtered so as not to change too rapidly. */
filterExposure(); filterExposure();
/* /*
* Some of the exposure has to be applied as digital gain, so work out * We may be asked to limit the exposure using other channels. If another channel
* what that is. This function also tells us whether it's decided to * determines our upper bound we may want to know this later.
* "desaturate" the image more quickly.
*/ */
bool desaturate = applyDigitalGain(gain, targetY); bool channelBound = applyChannelConstraints(channelTotalExposures);
/*
* Some of the exposure has to be applied as digital gain, so work out
* 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, channelBound);
/* /*
* The last thing is to divide up the exposure value into a shutter time * The last thing is to divide up the exposure value into a shutter time
* and analogue gain, according to the current exposure mode. * and analogue gain, according to the current exposure mode.
@ -794,7 +801,44 @@ void AgcChannel::computeTargetExposure(double gain)
LOG(RPiAgc, Debug) << "Target totalExposure " << target_.totalExposure; 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;
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 }); double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 });
ASSERT(minColourGain != 0.0); ASSERT(minColourGain != 0.0);
@ -814,8 +858,8 @@ bool AgcChannel::applyDigitalGain(double gain, double targetY)
* quickly (and we then approach the correct value more quickly from * quickly (and we then approach the correct value more quickly from
* below). * below).
*/ */
bool desaturate = targetY > config_.fastReduceThreshold && bool desaturate = !channelBound &&
gain < sqrt(targetY); targetY > config_.fastReduceThreshold && gain < sqrt(targetY);
if (desaturate) if (desaturate)
dg /= config_.fastReduceThreshold; dg /= config_.fastReduceThreshold;
LOG(RPiAgc, Debug) << "Digital gain " << dg << " desaturate? " << desaturate; LOG(RPiAgc, Debug) << "Digital gain " << dg << " desaturate? " << desaturate;

View file

@ -21,6 +21,8 @@
namespace RPiController { namespace RPiController {
using AgcChannelTotalExposures = std::vector<libcamera::utils::Duration>;
struct AgcMeteringMode { struct AgcMeteringMode {
std::vector<double> weights; std::vector<double> weights;
int read(const libcamera::YamlObject &params); int read(const libcamera::YamlObject &params);
@ -95,7 +97,8 @@ public:
void disableAuto(); void disableAuto();
void switchMode(CameraMode const &cameraMode, Metadata *metadata); void switchMode(CameraMode const &cameraMode, Metadata *metadata);
void prepare(Metadata *imageMetadata); void prepare(Metadata *imageMetadata);
void process(StatisticsPtr &stats, DeviceStatus const &deviceStatus, Metadata *imageMetadata); void process(StatisticsPtr &stats, DeviceStatus const &deviceStatus, Metadata *imageMetadata,
const AgcChannelTotalExposures &channelTotalExposures);
private: private:
bool updateLockStatus(DeviceStatus const &deviceStatus); bool updateLockStatus(DeviceStatus const &deviceStatus);
@ -107,7 +110,8 @@ private:
double &gain, double &targetY); double &gain, double &targetY);
void computeTargetExposure(double gain); void computeTargetExposure(double gain);
void filterExposure(); void filterExposure();
bool applyDigitalGain(double gain, double targetY); bool applyChannelConstraints(const AgcChannelTotalExposures &channelTotalExposures);
bool applyDigitalGain(double gain, double targetY, bool channelBound);
void divideUpExposure(); void divideUpExposure();
void writeAndFinish(Metadata *imageMetadata, bool desaturate); void writeAndFinish(Metadata *imageMetadata, bool desaturate);
libcamera::utils::Duration limitShutter(libcamera::utils::Duration shutter); libcamera::utils::Duration limitShutter(libcamera::utils::Duration shutter);