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:
parent
7927c44735
commit
7127954aaa
4 changed files with 76 additions and 15 deletions
|
@ -39,6 +39,7 @@ int Agc::read(const libcamera::YamlObject ¶ms)
|
||||||
*/
|
*/
|
||||||
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 ¶ms)
|
||||||
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();
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 ¶ms);
|
int read(const libcamera::YamlObject ¶ms);
|
||||||
|
@ -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);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue