summaryrefslogtreecommitdiff
path: root/src/ipa/raspberrypi/controller/rpi/agc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ipa/raspberrypi/controller/rpi/agc.cpp')
-rw-r--r--src/ipa/raspberrypi/controller/rpi/agc.cpp269
1 files changed, 161 insertions, 108 deletions
diff --git a/src/ipa/raspberrypi/controller/rpi/agc.cpp b/src/ipa/raspberrypi/controller/rpi/agc.cpp
index 52a41a55..5a282a42 100644
--- a/src/ipa/raspberrypi/controller/rpi/agc.cpp
+++ b/src/ipa/raspberrypi/controller/rpi/agc.cpp
@@ -28,7 +28,7 @@ LOG_DEFINE_CATEGORY(RPiAgc)
#define NAME "rpi.agc"
-#define PIPELINE_BITS 13 // seems to be a 13-bit pipeline
+#define PIPELINE_BITS 13 /* seems to be a 13-bit pipeline */
void AgcMeteringMode::read(boost::property_tree::ptree const &params)
{
@@ -150,7 +150,7 @@ void AgcConfig::read(boost::property_tree::ptree const &params)
convergenceFrames = params.get<unsigned int>("convergence_frames", 6);
fastReduceThreshold = params.get<double>("fast_reduce_threshold", 0.4);
baseEv = params.get<double>("base_ev", 1.0);
- // Start with quite a low value as ramping up is easier than ramping down.
+ /* Start with quite a low value as ramping up is easier than ramping down. */
defaultExposureTime = params.get<double>("default_exposure_time", 1000) * 1us;
defaultAnalogueGain = params.get<double>("default_analogueGain", 1.0);
}
@@ -170,8 +170,10 @@ Agc::Agc(Controller *controller)
maxShutter_(0s), fixedShutter_(0s), fixedAnalogueGain_(0.0)
{
memset(&awb_, 0, sizeof(awb_));
- // Setting status_.totalExposureValue_ to zero initially tells us
- // it's not been calculated yet (i.e. Process hasn't yet run).
+ /*
+ * Setting status_.totalExposureValue_ to zero initially tells us
+ * it's not been calculated yet (i.e. Process hasn't yet run).
+ */
memset(&status_, 0, sizeof(status_));
status_.ev = ev_;
}
@@ -185,16 +187,18 @@ void Agc::read(boost::property_tree::ptree const &params)
{
LOG(RPiAgc, Debug) << "Agc";
config_.read(params);
- // Set the config's defaults (which are the first ones it read) as our
- // current modes, until someone changes them. (they're all known to
- // exist at this point)
+ /*
+ * Set the config's defaults (which are the first ones it read) as our
+ * current modes, until someone changes them. (they're all known to
+ * exist at this point)
+ */
meteringModeName_ = config_.defaultMeteringMode;
meteringMode_ = &config_.meteringModes[meteringModeName_];
exposureModeName_ = config_.defaultExposureMode;
exposureMode_ = &config_.exposureModes[exposureModeName_];
constraintModeName_ = config_.defaultConstraintMode;
constraintMode_ = &config_.constraintModes[constraintModeName_];
- // Set up the "last shutter/gain" values, in case AGC starts "disabled".
+ /* Set up the "last shutter/gain" values, in case AGC starts "disabled". */
status_.shutterTime = config_.defaultExposureTime;
status_.analogueGain = config_.defaultAnalogueGain;
}
@@ -218,8 +222,10 @@ void Agc::resume()
unsigned int Agc::getConvergenceFrames() const
{
- // If shutter and gain have been explicitly set, there is no
- // convergence to happen, so no need to drop any frames - return zero.
+ /*
+ * If shutter and gain have been explicitly set, there is no
+ * convergence to happen, so no need to drop any frames - return zero.
+ */
if (fixedShutter_ && fixedAnalogueGain_)
return 0;
else
@@ -244,14 +250,14 @@ void Agc::setMaxShutter(Duration maxShutter)
void Agc::setFixedShutter(Duration fixedShutter)
{
fixedShutter_ = fixedShutter;
- // Set this in case someone calls Pause() straight after.
+ /* Set this in case someone calls Pause() straight after. */
status_.shutterTime = clipShutter(fixedShutter_);
}
void Agc::setFixedAnalogueGain(double fixedAnalogueGain)
{
fixedAnalogueGain_ = fixedAnalogueGain;
- // Set this in case someone calls Pause() straight after.
+ /* Set this in case someone calls Pause() straight after. */
status_.analogueGain = fixedAnalogueGain;
}
@@ -280,30 +286,32 @@ void Agc::switchMode(CameraMode const &cameraMode,
Duration fixedShutter = clipShutter(fixedShutter_);
if (fixedShutter && fixedAnalogueGain_) {
- // We're going to reset the algorithm here with these fixed values.
+ /* We're going to reset the algorithm here with these fixed values. */
fetchAwbStatus(metadata);
double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 });
ASSERT(minColourGain != 0.0);
- // This is the equivalent of computeTargetExposure and applyDigitalGain.
+ /* This is the equivalent of computeTargetExposure and applyDigitalGain. */
target_.totalExposureNoDG = fixedShutter_ * fixedAnalogueGain_;
target_.totalExposure = target_.totalExposureNoDG / minColourGain;
- // Equivalent of filterExposure. This resets any "history".
+ /* Equivalent of filterExposure. This resets any "history". */
filtered_ = target_;
- // Equivalent of divideUpExposure.
+ /* Equivalent of divideUpExposure. */
filtered_.shutter = fixedShutter;
filtered_.analogueGain = fixedAnalogueGain_;
} else if (status_.totalExposureValue) {
- // On a mode switch, various things could happen:
- // - the exposure profile might change
- // - a fixed exposure or gain might be set
- // - the new mode's sensitivity might be different
- // We cope with the last of these by scaling the target values. After
- // that we just need to re-divide the exposure/gain according to the
- // current exposure profile, which takes care of everything else.
+ /*
+ * On a mode switch, various things could happen:
+ * - the exposure profile might change
+ * - a fixed exposure or gain might be set
+ * - the new mode's sensitivity might be different
+ * We cope with the last of these by scaling the target values. After
+ * that we just need to re-divide the exposure/gain according to the
+ * current exposure profile, which takes care of everything else.
+ */
double ratio = lastSensitivity_ / cameraMode.sensitivity;
target_.totalExposureNoDG *= ratio;
@@ -313,29 +321,31 @@ void Agc::switchMode(CameraMode const &cameraMode,
divideUpExposure();
} else {
- // We come through here on startup, when at least one of the shutter
- // or gain has not been fixed. We must still write those values out so
- // that they will be applied immediately. We supply some arbitrary defaults
- // for any that weren't set.
-
- // Equivalent of divideUpExposure.
+ /*
+ * We come through here on startup, when at least one of the shutter
+ * or gain has not been fixed. We must still write those values out so
+ * that they will be applied immediately. We supply some arbitrary defaults
+ * for any that weren't set.
+ */
+
+ /* Equivalent of divideUpExposure. */
filtered_.shutter = fixedShutter ? fixedShutter : config_.defaultExposureTime;
filtered_.analogueGain = fixedAnalogueGain_ ? fixedAnalogueGain_ : config_.defaultAnalogueGain;
}
writeAndFinish(metadata, false);
- // We must remember the sensitivity of this mode for the next SwitchMode.
+ /* We must remember the sensitivity of this mode for the next SwitchMode. */
lastSensitivity_ = cameraMode.sensitivity;
}
void Agc::prepare(Metadata *imageMetadata)
{
status_.digitalGain = 1.0;
- fetchAwbStatus(imageMetadata); // always fetch it so that Process knows it's been done
+ fetchAwbStatus(imageMetadata); /* always fetch it so that Process knows it's been done */
if (status_.totalExposureValue) {
- // Process has run, so we have meaningful values.
+ /* Process has run, so we have meaningful values. */
DeviceStatus deviceStatus;
if (imageMetadata->get("device.status", deviceStatus) == 0) {
Duration actualExposure = deviceStatus.shutterSpeed *
@@ -343,14 +353,16 @@ void Agc::prepare(Metadata *imageMetadata)
if (actualExposure) {
status_.digitalGain = status_.totalExposureValue / actualExposure;
LOG(RPiAgc, Debug) << "Want total exposure " << status_.totalExposureValue;
- // Never ask for a gain < 1.0, and also impose
- // some upper limit. Make it customisable?
+ /*
+ * Never ask for a gain < 1.0, and also impose
+ * some upper limit. Make it customisable?
+ */
status_.digitalGain = std::max(1.0, std::min(status_.digitalGain, 4.0));
LOG(RPiAgc, Debug) << "Actual exposure " << actualExposure;
LOG(RPiAgc, Debug) << "Use digitalGain " << status_.digitalGain;
LOG(RPiAgc, Debug) << "Effective exposure "
<< actualExposure * status_.digitalGain;
- // Decide whether AEC/AGC has converged.
+ /* Decide whether AEC/AGC has converged. */
updateLockStatus(deviceStatus);
}
} else
@@ -362,44 +374,52 @@ void Agc::prepare(Metadata *imageMetadata)
void Agc::process(StatisticsPtr &stats, Metadata *imageMetadata)
{
frameCount_++;
- // First a little bit of housekeeping, fetching up-to-date settings and
- // configuration, that kind of thing.
+ /*
+ * First a little bit of housekeeping, fetching up-to-date settings and
+ * configuration, that kind of thing.
+ */
housekeepConfig();
- // Get the current exposure values for the frame that's just arrived.
+ /* Get the current exposure values for the frame that's just arrived. */
fetchCurrentExposure(imageMetadata);
- // Compute the total gain we require relative to the current exposure.
+ /* Compute the total gain we require relative to the current exposure. */
double gain, targetY;
computeGain(stats.get(), imageMetadata, gain, targetY);
- // Now compute the target (final) exposure which we think we want.
+ /* Now compute the target (final) exposure which we think we want. */
computeTargetExposure(gain);
- // 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.
+ /*
+ * 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.
+ */
bool desaturate = applyDigitalGain(gain, targetY);
- // 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(desaturate);
- // The last thing is to divide up the exposure value into a shutter time
- // and analogue gain, according to the current exposure mode.
+ /*
+ * The last thing is to divide up the exposure value into a shutter time
+ * and analogue gain, according to the current exposure mode.
+ */
divideUpExposure();
- // Finally advertise what we've done.
+ /* Finally advertise what we've done. */
writeAndFinish(imageMetadata, desaturate);
}
void Agc::updateLockStatus(DeviceStatus const &deviceStatus)
{
- const double errorFactor = 0.10; // make these customisable?
+ const double errorFactor = 0.10; /* make these customisable? */
const int maxLockCount = 5;
- // Reset "lock count" when we exceed this multiple of errorFactor
+ /* Reset "lock count" when we exceed this multiple of errorFactor */
const double resetMargin = 1.5;
- // Add 200us to the exposure time error to allow for line quantisation.
+ /* Add 200us to the exposure time error to allow for line quantisation. */
Duration exposureError = lastDeviceStatus_.shutterSpeed * errorFactor + 200us;
double gainError = lastDeviceStatus_.analogueGain * errorFactor;
Duration targetError = lastTargetExposure_ * errorFactor;
- // Note that we don't know the exposure/gain limits of the sensor, so
- // the values we keep requesting may be unachievable. For this reason
- // we only insist that we're close to values in the past few frames.
+ /*
+ * Note that we don't know the exposure/gain limits of the sensor, so
+ * the values we keep requesting may be unachievable. For this reason
+ * we only insist that we're close to values in the past few frames.
+ */
if (deviceStatus.shutterSpeed > lastDeviceStatus_.shutterSpeed - exposureError &&
deviceStatus.shutterSpeed < lastDeviceStatus_.shutterSpeed + exposureError &&
deviceStatus.analogueGain > lastDeviceStatus_.analogueGain - gainError &&
@@ -430,7 +450,7 @@ static void copyString(std::string const &s, char *d, size_t size)
void Agc::housekeepConfig()
{
- // First fetch all the up-to-date settings, so no one else has to do it.
+ /* First fetch all the up-to-date settings, so no one else has to do it. */
status_.ev = ev_;
status_.fixedShutter = clipShutter(fixedShutter_);
status_.fixedAnalogueGain = fixedAnalogueGain_;
@@ -438,8 +458,10 @@ void Agc::housekeepConfig()
LOG(RPiAgc, Debug) << "ev " << status_.ev << " fixedShutter "
<< status_.fixedShutter << " fixedAnalogueGain "
<< status_.fixedAnalogueGain;
- // Make sure the "mode" pointers point to the up-to-date things, if
- // they've changed.
+ /*
+ * Make sure the "mode" pointers point to the up-to-date things, if
+ * they've changed.
+ */
if (strcmp(meteringModeName_.c_str(), status_.meteringMode)) {
auto it = config_.meteringModes.find(meteringModeName_);
if (it == config_.meteringModes.end())
@@ -491,7 +513,7 @@ void Agc::fetchCurrentExposure(Metadata *imageMetadata)
void Agc::fetchAwbStatus(Metadata *imageMetadata)
{
- awb_.gainR = 1.0; // in case not found in metadata
+ awb_.gainR = 1.0; /* in case not found in metadata */
awb_.gainG = 1.0;
awb_.gainB = 1.0;
if (imageMetadata->get("awb.status", awb_) != 0)
@@ -502,8 +524,10 @@ static double computeInitialY(bcm2835_isp_stats *stats, AwbStatus const &awb,
double weights[], double gain)
{
bcm2835_isp_stats_region *regions = stats->agc_stats;
- // Note how the calculation below means that equal weights give you
- // "average" metering (i.e. all pixels equally important).
+ /*
+ * Note how the calculation below means that equal weights give you
+ * "average" metering (i.e. all pixels equally important).
+ */
double rSum = 0, gSum = 0, bSum = 0, pixelSum = 0;
for (int i = 0; i < AGC_STATS_SIZE; i++) {
double counted = regions[i].counted;
@@ -525,11 +549,13 @@ static double computeInitialY(bcm2835_isp_stats *stats, AwbStatus const &awb,
return ySum / pixelSum / (1 << PIPELINE_BITS);
}
-// We handle extra gain through EV by adjusting our Y targets. However, you
-// simply can't monitor histograms once they get very close to (or beyond!)
-// saturation, so we clamp the Y targets to this value. It does mean that EV
-// increases don't necessarily do quite what you might expect in certain
-// (contrived) cases.
+/*
+ * We handle extra gain through EV by adjusting our Y targets. However, you
+ * simply can't monitor histograms once they get very close to (or beyond!)
+ * saturation, so we clamp the Y targets to this value. It does mean that EV
+ * increases don't necessarily do quite what you might expect in certain
+ * (contrived) cases.
+ */
#define EV_GAIN_Y_TARGET_LIMIT 0.9
@@ -546,18 +572,22 @@ void Agc::computeGain(bcm2835_isp_stats *statistics, Metadata *imageMetadata,
double &gain, double &targetY)
{
struct LuxStatus lux = {};
- lux.lux = 400; // default lux level to 400 in case no metadata found
+ lux.lux = 400; /* default lux level to 400 in case no metadata found */
if (imageMetadata->get("lux.status", lux) != 0)
LOG(RPiAgc, Warning) << "Agc: no lux level found";
Histogram h(statistics->hist[0].g_hist, NUM_HISTOGRAM_BINS);
double evGain = status_.ev * config_.baseEv;
- // The initial gain and target_Y come from some of the regions. After
- // that we consider the histogram constraints.
+ /*
+ * The initial gain and target_Y come from some of the regions. After
+ * that we consider the histogram constraints.
+ */
targetY = config_.yTarget.eval(config_.yTarget.domain().clip(lux.lux));
targetY = std::min(EV_GAIN_Y_TARGET_LIMIT, targetY * evGain);
- // Do this calculation a few times as brightness increase can be
- // non-linear when there are saturated regions.
+ /*
+ * Do this calculation a few times as brightness increase can be
+ * non-linear when there are saturated regions.
+ */
gain = 1.0;
for (int i = 0; i < 8; i++) {
double initialY = computeInitialY(statistics, awb_, meteringMode_->weights, gain);
@@ -565,7 +595,7 @@ void Agc::computeGain(bcm2835_isp_stats *statistics, Metadata *imageMetadata,
gain *= extraGain;
LOG(RPiAgc, Debug) << "Initial Y " << initialY << " target " << targetY
<< " gives gain " << gain;
- if (extraGain < 1.01) // close enough
+ if (extraGain < 1.01) /* close enough */
break;
}
@@ -592,20 +622,23 @@ void Agc::computeGain(bcm2835_isp_stats *statistics, Metadata *imageMetadata,
void Agc::computeTargetExposure(double gain)
{
if (status_.fixedShutter && status_.fixedAnalogueGain) {
- // When ag and shutter are both fixed, we need to drive the
- // total exposure so that we end up with a digital gain of at least
- // 1/minColourGain. Otherwise we'd desaturate channels causing
- // white to go cyan or magenta.
+ /*
+ * When ag and shutter are both fixed, we need to drive the
+ * total exposure so that we end up with a digital gain of at least
+ * 1/minColourGain. Otherwise we'd desaturate channels causing
+ * white to go cyan or magenta.
+ */
double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 });
ASSERT(minColourGain != 0.0);
target_.totalExposure =
status_.fixedShutter * status_.fixedAnalogueGain / minColourGain;
} else {
- // The statistics reflect the image without digital gain, so the final
- // total exposure we're aiming for is:
+ /*
+ * The statistics reflect the image without digital gain, so the final
+ * total exposure we're aiming for is:
+ */
target_.totalExposure = current_.totalExposureNoDG * gain;
- // The final target exposure is also limited to what the exposure
- // mode allows.
+ /* The final target exposure is also limited to what the exposure mode allows. */
Duration maxShutter = status_.fixedShutter
? status_.fixedShutter
: exposureMode_->shutter.back();
@@ -625,17 +658,21 @@ bool Agc::applyDigitalGain(double gain, double targetY)
double minColourGain = std::min({ awb_.gainR, awb_.gainG, awb_.gainB, 1.0 });
ASSERT(minColourGain != 0.0);
double dg = 1.0 / minColourGain;
- // I think this pipeline subtracts black level and rescales before we
- // get the stats, so no need to worry about it.
+ /*
+ * I think this pipeline subtracts black level and rescales before we
+ * get the stats, so no need to worry about it.
+ */
LOG(RPiAgc, Debug) << "after AWB, target dg " << dg << " gain " << gain
<< " target_Y " << targetY;
- // Finally, if we're trying to reduce exposure but the target_Y is
- // "close" to 1.0, then the gain computed for that constraint will be
- // only slightly less than one, because the measured Y can never be
- // larger than 1.0. When this happens, demand a large digital gain so
- // that the exposure can be reduced, de-saturating the image much more
- // quickly (and we then approach the correct value more quickly from
- // below).
+ /*
+ * Finally, if we're trying to reduce exposure but the target_Y is
+ * "close" to 1.0, then the gain computed for that constraint will be
+ * only slightly less than one, because the measured Y can never be
+ * larger than 1.0. When this happens, demand a large digital gain so
+ * that the exposure can be reduced, de-saturating the image much more
+ * quickly (and we then approach the correct value more quickly from
+ * below).
+ */
bool desaturate = targetY > config_.fastReduceThreshold &&
gain < sqrt(targetY);
if (desaturate)
@@ -649,8 +686,10 @@ bool Agc::applyDigitalGain(double gain, double targetY)
void Agc::filterExposure(bool desaturate)
{
double speed = config_.speed;
- // AGC adapts instantly if both shutter and gain are directly specified
- // or we're in the startup phase.
+ /*
+ * AGC adapts instantly if both shutter and gain are directly specified
+ * or we're in the startup phase.
+ */
if ((status_.fixedShutter && status_.fixedAnalogueGain) ||
frameCount_ <= config_.startupFrames)
speed = 1.0;
@@ -658,15 +697,19 @@ void Agc::filterExposure(bool desaturate)
filtered_.totalExposure = target_.totalExposure;
filtered_.totalExposureNoDG = target_.totalExposureNoDG;
} else {
- // If close to the result go faster, to save making so many
- // micro-adjustments on the way. (Make this customisable?)
+ /*
+ * If close to the result go faster, to save making so many
+ * micro-adjustments on the way. (Make this customisable?)
+ */
if (filtered_.totalExposure < 1.2 * target_.totalExposure &&
filtered_.totalExposure > 0.8 * target_.totalExposure)
speed = sqrt(speed);
filtered_.totalExposure = speed * target_.totalExposure +
filtered_.totalExposure * (1.0 - speed);
- // When desaturing, take a big jump down in totalExposureNoDG,
- // which we'll hide with digital gain.
+ /*
+ * When desaturing, take a big jump down in totalExposureNoDG,
+ * which we'll hide with digital gain.
+ */
if (desaturate)
filtered_.totalExposureNoDG =
target_.totalExposureNoDG;
@@ -675,9 +718,11 @@ void Agc::filterExposure(bool desaturate)
speed * target_.totalExposureNoDG +
filtered_.totalExposureNoDG * (1.0 - speed);
}
- // We can't let the totalExposureNoDG exposure deviate too far below the
- // total exposure, as there might not be enough digital gain available
- // in the ISP to hide it (which will cause nasty oscillation).
+ /*
+ * We can't let the totalExposureNoDG exposure deviate too far below the
+ * total exposure, as there might not be enough digital gain available
+ * in the ISP to hide it (which will cause nasty oscillation).
+ */
if (filtered_.totalExposureNoDG <
filtered_.totalExposure * config_.fastReduceThreshold)
filtered_.totalExposureNoDG = filtered_.totalExposure * config_.fastReduceThreshold;
@@ -687,9 +732,11 @@ void Agc::filterExposure(bool desaturate)
void Agc::divideUpExposure()
{
- // Sending the fixed shutter/gain cases through the same code may seem
- // unnecessary, but it will make more sense when extend this to cover
- // variable aperture.
+ /*
+ * Sending the fixed shutter/gain cases through the same code may seem
+ * unnecessary, but it will make more sense when extend this to cover
+ * variable aperture.
+ */
Duration exposureValue = filtered_.totalExposureNoDG;
Duration shutterTime;
double analogueGain;
@@ -721,18 +768,22 @@ void Agc::divideUpExposure()
}
LOG(RPiAgc, Debug) << "Divided up shutter and gain are " << shutterTime << " and "
<< analogueGain;
- // Finally adjust shutter time for flicker avoidance (require both
- // shutter and gain not to be fixed).
+ /*
+ * Finally adjust shutter time for flicker avoidance (require both
+ * shutter and gain not to be fixed).
+ */
if (!status_.fixedShutter && !status_.fixedAnalogueGain &&
status_.flickerPeriod) {
int flickerPeriods = shutterTime / status_.flickerPeriod;
if (flickerPeriods) {
Duration newShutterTime = flickerPeriods * status_.flickerPeriod;
analogueGain *= shutterTime / newShutterTime;
- // We should still not allow the ag to go over the
- // largest value in the exposure mode. Note that this
- // may force more of the total exposure into the digital
- // gain as a side-effect.
+ /*
+ * We should still not allow the ag to go over the
+ * largest value in the exposure mode. Note that this
+ * may force more of the total exposure into the digital
+ * gain as a side-effect.
+ */
analogueGain = std::min(analogueGain, exposureMode_->gain.back());
shutterTime = newShutterTime;
}
@@ -749,8 +800,10 @@ void Agc::writeAndFinish(Metadata *imageMetadata, bool desaturate)
status_.targetExposureValue = desaturate ? 0s : target_.totalExposureNoDG;
status_.shutterTime = filtered_.shutter;
status_.analogueGain = filtered_.analogueGain;
- // Write to metadata as well, in case anyone wants to update the camera
- // immediately.
+ /*
+ * Write to metadata as well, in case anyone wants to update the camera
+ * immediately.
+ */
imageMetadata->set("agc.status", status_);
LOG(RPiAgc, Debug) << "Output written, total exposure requested is "
<< filtered_.totalExposure;
@@ -765,7 +818,7 @@ Duration Agc::clipShutter(Duration shutter)
return shutter;
}
-// Register algorithm with the system.
+/* Register algorithm with the system. */
static Algorithm *create(Controller *controller)
{
return (Algorithm *)new Agc(controller);