@@ -26,6 +26,8 @@
#include <libcamera/ipa/core_ipa_interface.h>
+#include "exposure_mode_helper.h"
+
/**
* \file agc.h
* \brief libipa AGC algorithm
@@ -222,6 +224,253 @@ namespace agc {
} /* namespace agc */
+namespace {
+
+/*
+ * Default relative luminance target
+ *
+ * This value should be chosen so that when the camera points at a grey target,
+ * the resulting image brightness looks "right". Custom values can be passed
+ * as the relativeLuminanceTarget value in sensor tuning files.
+ */
+constexpr double kDefaultRelativeLuminanceTarget = 0.16;
+
+int parseRelativeLuminanceTarget(AgcMeanLuminance::Configuration &config, const ValueNode &tuningData)
+{
+ auto &target = tuningData;
+ if (!target) {
+ config.relativeLuminanceTarget = { { { { 0.0, kDefaultRelativeLuminanceTarget } } } };
+ return 0;
+ }
+
+ auto pwl = target.get<Pwl>();
+ if (!pwl) {
+ LOG(Agc, Error)
+ << "Failed to load relative luminance target";
+ return -EINVAL;
+ }
+
+ config.relativeLuminanceTarget = std::move(*pwl);
+ return 0;
+}
+
+int parseConstraint(AgcMeanLuminance::Configuration &config, const ValueNode &modeDict, int32_t id)
+{
+ for (const auto &[boundName, content] : modeDict.asDict()) {
+ if (boundName != "upper" && boundName != "lower") {
+ LOG(Agc, Warning)
+ << "Ignoring unknown constraint bound '" << boundName << "'";
+ continue;
+ }
+
+ unsigned int idx = static_cast<unsigned int>(boundName == "upper");
+ auto bound = static_cast<AgcMeanLuminance::AgcConstraint::Bound>(idx);
+ double qLo = content["qLo"].get<double>().value_or(0.98);
+ double qHi = content["qHi"].get<double>().value_or(1.0);
+ auto yTarget = content["yTarget"].get<Pwl>();
+ if (!yTarget) {
+ LOG(Agc, Error)
+ << "Failed to parse yTarget";
+ return -EINVAL;
+ }
+
+ AgcMeanLuminance::AgcConstraint constraint = {
+ bound,
+ qLo,
+ qHi,
+ std::move(*yTarget),
+ };
+ auto &constraints = config.constraintModes[id];
+
+ if (idx)
+ constraints.push_back(std::move(constraint));
+ else
+ constraints.insert(constraints.begin(), std::move(constraint));
+ }
+
+ return 0;
+}
+
+int parseConstraintModes(AgcMeanLuminance::Configuration &config, const ValueNode &constraintModes)
+{
+ if (!constraintModes.isDictionary())
+ return 0;
+
+ for (const auto &[modeName, modeDict] : constraintModes.asDict()) {
+ auto it = controls::AeConstraintModeNameValueMap.find(modeName);
+ if (it == controls::AeConstraintModeNameValueMap.end()) {
+ LOG(Agc, Warning)
+ << "Skipping unknown constraint mode '" << modeName << "'";
+ continue;
+ }
+
+ if (!modeDict.isDictionary()) {
+ LOG(Agc, Error)
+ << "Invalid constraint mode '" << modeName << "'";
+ return -EINVAL;
+ }
+
+ int ret = parseConstraint(config, modeDict, it->second);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int parseExposureModes(AgcMeanLuminance::Configuration &config, const ValueNode &exposureModes)
+{
+ if (!exposureModes.isDictionary())
+ return 0;
+
+ for (const auto &[modeName, modeValues] : exposureModes.asDict()) {
+ auto it = controls::AeExposureModeNameValueMap.find(modeName);
+ if (it == controls::AeExposureModeNameValueMap.end()) {
+ LOG(Agc, Warning)
+ << "Skipping unknown exposure mode '" << modeName << "'";
+ continue;
+ }
+
+ if (!modeValues.isDictionary()) {
+ LOG(Agc, Error)
+ << "Invalid exposure mode '" << modeName << "'";
+ return -EINVAL;
+ }
+
+ std::vector<uint32_t> exposureTimes =
+ modeValues["exposureTime"].get<std::vector<uint32_t>>().value_or(utils::defopt);
+ std::vector<double> gains =
+ modeValues["gain"].get<std::vector<double>>().value_or(utils::defopt);
+
+ if (exposureTimes.size() != gains.size()) {
+ LOG(Agc, Error)
+ << "Exposure time and gain array sizes unequal";
+ return -EINVAL;
+ }
+
+ if (exposureTimes.empty()) {
+ LOG(Agc, Error)
+ << "Exposure time and gain arrays are empty";
+ return -EINVAL;
+ }
+
+ std::vector<ExposureModeHelper::Stage> stages;
+ for (size_t i = 0; i < exposureTimes.size(); i++) {
+ stages.push_back({
+ .exposureTime = std::chrono::microseconds(exposureTimes[i]),
+ .gain = gains[i],
+ });
+ }
+
+ config.exposureModeHelpers.try_emplace(it->second, std::move(stages));
+ }
+
+ return 0;
+}
+
+/**
+ * \brief Parse tuning data for AeConstraintMode and AeExposureMode controls
+ * \param[in] tuningData the ValueNode representing the tuning data
+ *
+ * This function parses tuning data to build the list of allowed values for the
+ * AeConstraintMode and AeExposureMode controls. Those tuning data must provide
+ * the data in a specific format; the Agc algorithm's tuning data should contain
+ * a dictionary called AeConstraintMode containing per-mode setting dictionaries
+ * with the key being a value from \ref controls::AeConstraintModeNameValueMap.
+ * The yTarget can either be provided as single value or as array in which case
+ * it is interpreted as a PWL mapping lux levels to yTarget values. Each mode
+ * dict may contain either a "lower" or "upper" key or both, for example:
+ *
+ * \code{.unparsed}
+ * algorithms:
+ * - Agc:
+ * AeConstraintMode:
+ * ConstraintNormal:
+ * lower:
+ * qLo: 0.98
+ * qHi: 1.0
+ * yTarget: 0.5
+ * ConstraintHighlight:
+ * lower:
+ * qLo: 0.98
+ * qHi: 1.0
+ * yTarget: 0.5
+ * upper:
+ * qLo: 0.98
+ * qHi: 1.0
+ * yTarget: [ 100, 0.8, 20000, 0.5 ]
+ *
+ * \endcode
+ *
+ * For the AeExposureMode control the data should contain a dictionary called
+ * AeExposureMode containing per-mode setting dictionaries with the key being a
+ * value from \ref controls::AeExposureModeNameValueMap. Each mode dict should
+ * contain an array of exposure times with the key "exposureTime" and an array
+ * of gain values with the key "gain", in this format:
+ *
+ * \code{.unparsed}
+ * algorithms:
+ * - Agc:
+ * AeExposureMode:
+ * ExposureNormal:
+ * exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
+ * gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
+ * ExposureShort:
+ * exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
+ * gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
+ *
+ * \endcode
+ *
+ * \return 0 on success or a negative error code
+ */
+int parseTuningData(AgcMeanLuminance::Configuration &config, const ValueNode &tuningData)
+{
+ int ret;
+
+ ret = parseRelativeLuminanceTarget(config, tuningData["relativeLuminanceTarget"]);
+ if (ret)
+ return ret;
+
+ ret = parseConstraintModes(config, tuningData[controls::AeConstraintMode.name()]);
+ if (ret)
+ return ret;
+
+ return parseExposureModes(config, tuningData[controls::AeExposureMode.name()]);
+}
+
+void fillMissing(AgcMeanLuminance::Configuration &config)
+{
+ /*
+ * If we don't have any exposure modes in the tuning data we create an
+ * ExposureModeHelper using an empty vector of stages. This will result
+ * in the ExposureModeHelper simply driving the exposure time as high as
+ * possible before touching gain.
+ */
+ if (config.exposureModeHelpers.empty())
+ config.exposureModeHelpers.try_emplace(
+ controls::ExposureNormal,
+ std::vector<ExposureModeHelper::Stage>{});
+
+ /*
+ * If the tuning data file contains no constraints then we use the
+ * default constraint that the IPU3/RkISP1 Agc algorithms were adhering
+ * to anyway before centralisation; this constraint forces the top 2% of
+ * the histogram to be at least 0.5.
+ */
+ if (config.constraintModes.empty()) {
+ AgcMeanLuminance::AgcConstraint constraint = {
+ AgcMeanLuminance::AgcConstraint::Bound::Lower,
+ 0.98,
+ 1.0,
+ Pwl({ { { 0.0, 0.5 } } }),
+ };
+
+ config.constraintModes[controls::ConstraintNormal].push_back(std::move(constraint));
+ }
+}
+
+} /* namespace */
+
/**
* \class AgcAlgorithm
* \brief libIPA LSC algorithm algorithm
@@ -310,10 +559,14 @@ int AgcAlgorithm::init(const ValueNode &tuningData, CameraSensorHelper *sensor,
const ConfigurationParams &config)
{
if (sensor) {
- auto &impl = impl_.emplace<AgcMeanLuminance>();
- int ret = impl.parseTuningData(tuningData);
+ AgcMeanLuminance::Configuration agcMLconfig;
+ int ret = parseTuningData(agcMLconfig, tuningData);
if (ret)
return ret;
+
+ fillMissing(agcMLconfig);
+
+ impl_.emplace<AgcMeanLuminance>(std::move(agcMLconfig));
} else {
impl_.emplace<AgcMSV>();
}
@@ -9,7 +9,6 @@
#include <algorithm>
#include <cmath>
-#include <span>
#include <libcamera/base/log.h>
#include <libcamera/base/utils.h>
@@ -18,8 +17,6 @@
#include "exposure_mode_helper.h"
-using namespace libcamera::controls;
-
/**
* \file agc_mean_luminance.h
* \brief Class implementing mean luminance AEGC
@@ -39,15 +36,6 @@ namespace ipa {
*/
static constexpr uint32_t kNumStartupFrames = 10;
-/*
- * Default relative luminance target
- *
- * This value should be chosen so that when the camera points at a grey target,
- * the resulting image brightness looks "right". Custom values can be passed
- * as the relativeLuminanceTarget value in sensor tuning files.
- */
-static constexpr double kDefaultRelativeLuminanceTarget = 0.16;
-
/*
* Maximum relative luminance target
*
@@ -177,168 +165,18 @@ static constexpr unsigned int kDefaultLuxLevel = 500;
* setLimits() must be called again with the new values.
*/
-AgcMeanLuminance::AgcMeanLuminance()
- : filteredExposure_(0s), luxWarningEnabled_(true), frameCount_(0)
+AgcMeanLuminance::AgcMeanLuminance(Configuration &&config)
+ : filteredExposure_(0s), luxWarningEnabled_(true), frameCount_(0),
+ relativeLuminanceTarget_(std::move(config.relativeLuminanceTarget)),
+ constraintModes_(std::move(config.constraintModes)),
+ exposureModeHelpers_(std::move(config.exposureModeHelpers))
{
+ ASSERT(!constraintModes_.empty());
+ ASSERT(!exposureModeHelpers_.empty());
}
AgcMeanLuminance::~AgcMeanLuminance() = default;
-int AgcMeanLuminance::parseRelativeLuminanceTarget(const ValueNode &tuningData)
-{
- auto &target = tuningData["relativeLuminanceTarget"];
- if (!target) {
- relativeLuminanceTarget_ = { { { { 0.0, kDefaultRelativeLuminanceTarget } } } };
- return 0;
- }
-
- auto pwl = target.get<Pwl>();
- if (!pwl) {
- LOG(AgcMeanLuminance, Error)
- << "Failed to load relative luminance target.";
- return -EINVAL;
- }
-
- relativeLuminanceTarget_ = std::move(*pwl);
- return 0;
-}
-
-int AgcMeanLuminance::parseConstraint(const ValueNode &modeDict, int32_t id)
-{
- for (const auto &[boundName, content] : modeDict.asDict()) {
- if (boundName != "upper" && boundName != "lower") {
- LOG(AgcMeanLuminance, Warning)
- << "Ignoring unknown constraint bound '" << boundName << "'";
- continue;
- }
-
- unsigned int idx = static_cast<unsigned int>(boundName == "upper");
- AgcConstraint::Bound bound = static_cast<AgcConstraint::Bound>(idx);
- double qLo = content["qLo"].get<double>().value_or(0.98);
- double qHi = content["qHi"].get<double>().value_or(1.0);
- auto yTarget = content["yTarget"].get<Pwl>();
- if (!yTarget) {
- LOG(AgcMeanLuminance, Error)
- << "Failed to parse yTarget";
- return -EINVAL;
- }
-
- AgcConstraint constraint = { bound, qLo, qHi, std::move(*yTarget) };
- auto &constraints = constraintModes_[id];
-
- if (idx)
- constraints.push_back(std::move(constraint));
- else
- constraints.insert(constraints.begin(), std::move(constraint));
- }
-
- return 0;
-}
-
-int AgcMeanLuminance::parseConstraintModes(const ValueNode &tuningData)
-{
- const ValueNode &constraintModes = tuningData[controls::AeConstraintMode.name()];
- if (constraintModes.isDictionary()) {
- for (const auto &[modeName, modeDict] : constraintModes.asDict()) {
- auto it = AeConstraintModeNameValueMap.find(modeName);
- if (it == AeConstraintModeNameValueMap.end()) {
- LOG(AgcMeanLuminance, Warning)
- << "Skipping unknown constraint mode '" << modeName << "'";
- continue;
- }
-
- if (!modeDict.isDictionary()) {
- LOG(AgcMeanLuminance, Error)
- << "Invalid constraint mode '" << modeName << "'";
- return -EINVAL;
- }
-
- int ret = parseConstraint(modeDict, it->second);
- if (ret)
- return ret;
- }
- }
-
- /*
- * If the tuning data file contains no constraints then we use the
- * default constraint that the IPU3/RkISP1 Agc algorithms were adhering
- * to anyway before centralisation; this constraint forces the top 2% of
- * the histogram to be at least 0.5.
- */
- if (constraintModes_.empty()) {
- AgcConstraint constraint = {
- AgcConstraint::Bound::Lower,
- 0.98,
- 1.0,
- Pwl({ { { 0.0, 0.5 } } })
- };
-
- constraintModes_[controls::ConstraintNormal].push_back(std::move(constraint));
- }
-
- return 0;
-}
-
-int AgcMeanLuminance::parseExposureModes(const ValueNode &tuningData)
-{
- const ValueNode &exposureModes = tuningData[controls::AeExposureMode.name()];
- if (exposureModes.isDictionary()) {
- for (const auto &[modeName, modeValues] : exposureModes.asDict()) {
- auto it = AeExposureModeNameValueMap.find(modeName);
- if (it == AeExposureModeNameValueMap.end()) {
- LOG(AgcMeanLuminance, Warning)
- << "Skipping unknown exposure mode '" << modeName << "'";
- continue;
- }
-
- if (!modeValues.isDictionary()) {
- LOG(AgcMeanLuminance, Error)
- << "Invalid exposure mode '" << modeName << "'";
- return -EINVAL;
- }
-
- std::vector<uint32_t> exposureTimes =
- modeValues["exposureTime"].get<std::vector<uint32_t>>().value_or(utils::defopt);
- std::vector<double> gains =
- modeValues["gain"].get<std::vector<double>>().value_or(utils::defopt);
-
- if (exposureTimes.size() != gains.size()) {
- LOG(AgcMeanLuminance, Error)
- << "Exposure time and gain array sizes unequal";
- return -EINVAL;
- }
-
- if (exposureTimes.empty()) {
- LOG(AgcMeanLuminance, Error)
- << "Exposure time and gain arrays are empty";
- return -EINVAL;
- }
-
- std::vector<ExposureModeHelper::Stage> stages;
- for (size_t i = 0; i < exposureTimes.size(); i++) {
- stages.push_back({
- .exposureTime = std::chrono::microseconds(exposureTimes[i]),
- .gain = gains[i],
- });
- }
-
- exposureModeHelpers_.try_emplace(it->second, std::move(stages));
- }
- }
-
- /*
- * If we don't have any exposure modes in the tuning data we create an
- * ExposureModeHelper using an empty vector of stages. This will result
- * in the ExposureModeHelper simply driving the exposure time as high as
- * possible before touching gain.
- */
- if (exposureModeHelpers_.empty())
- exposureModeHelpers_.try_emplace(controls::ExposureNormal,
- std::vector<ExposureModeHelper::Stage>{});
-
- return 0;
-}
-
/**
* \brief Configure the exposure mode helpers
* \param[in] lineDuration The sensor line length
@@ -358,76 +196,6 @@ void AgcMeanLuminance::configure(utils::Duration lineDuration,
frameCount_ = 0;
}
-/**
- * \brief Parse tuning data for AeConstraintMode and AeExposureMode controls
- * \param[in] tuningData the ValueNode representing the tuning data
- *
- * This function parses tuning data to build the list of allowed values for the
- * AeConstraintMode and AeExposureMode controls. Those tuning data must provide
- * the data in a specific format; the Agc algorithm's tuning data should contain
- * a dictionary called AeConstraintMode containing per-mode setting dictionaries
- * with the key being a value from \ref controls::AeConstraintModeNameValueMap.
- * The yTarget can either be provided as single value or as array in which case
- * it is interpreted as a PWL mapping lux levels to yTarget values. Each mode
- * dict may contain either a "lower" or "upper" key or both, for example:
- *
- * \code{.unparsed}
- * algorithms:
- * - Agc:
- * AeConstraintMode:
- * ConstraintNormal:
- * lower:
- * qLo: 0.98
- * qHi: 1.0
- * yTarget: 0.5
- * ConstraintHighlight:
- * lower:
- * qLo: 0.98
- * qHi: 1.0
- * yTarget: 0.5
- * upper:
- * qLo: 0.98
- * qHi: 1.0
- * yTarget: [ 100, 0.8, 20000, 0.5 ]
- *
- * \endcode
- *
- * For the AeExposureMode control the data should contain a dictionary called
- * AeExposureMode containing per-mode setting dictionaries with the key being a
- * value from \ref controls::AeExposureModeNameValueMap. Each mode dict should
- * contain an array of exposure times with the key "exposureTime" and an array
- * of gain values with the key "gain", in this format:
- *
- * \code{.unparsed}
- * algorithms:
- * - Agc:
- * AeExposureMode:
- * ExposureNormal:
- * exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
- * gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
- * ExposureShort:
- * exposureTime: [ 100, 10000, 30000, 60000, 120000 ]
- * gain: [ 2.0, 4.0, 6.0, 8.0, 10.0 ]
- *
- * \endcode
- *
- * \return 0 on success or a negative error code
- */
-int AgcMeanLuminance::parseTuningData(const ValueNode &tuningData)
-{
- int ret;
-
- ret = parseRelativeLuminanceTarget(tuningData);
- if (ret)
- return ret;
-
- ret = parseConstraintModes(tuningData);
- if (ret)
- return ret;
-
- return parseExposureModes(tuningData);
-}
-
/**
* \brief Set the ExposureModeHelper limits for this class
* \param[in] minExposureTime Minimum exposure time to allow
@@ -8,13 +8,10 @@
#pragma once
#include <map>
-#include <memory>
#include <vector>
#include <libcamera/base/utils.h>
-#include "libcamera/internal/value_node.h"
-
#include "exposure_mode_helper.h"
#include "histogram.h"
#include "pwl.h"
@@ -26,9 +23,6 @@ namespace ipa {
class AgcMeanLuminance
{
public:
- AgcMeanLuminance();
- ~AgcMeanLuminance();
-
struct AgcConstraint {
enum class Bound {
Lower = 0,
@@ -40,6 +34,15 @@ public:
Pwl yTarget;
};
+ struct Configuration {
+ Pwl relativeLuminanceTarget;
+ std::map<int32_t, std::vector<AgcConstraint>> constraintModes;
+ std::map<int32_t, ExposureModeHelper> exposureModeHelpers;
+ };
+
+ AgcMeanLuminance(Configuration &&config);
+ ~AgcMeanLuminance();
+
struct Traits {
virtual ~Traits() = default;
virtual double estimateLuminance(double gain) const = 0;
@@ -80,20 +83,17 @@ public:
double effectiveYTarget(double lux, double exposureCompensation) const;
private:
- int parseRelativeLuminanceTarget(const ValueNode &tuningData);
- int parseConstraint(const ValueNode &modeDict, int32_t id);
- int parseConstraintModes(const ValueNode &tuningData);
- int parseExposureModes(const ValueNode &tuningData);
double estimateInitialGain(const Traits &traits, double yTarget) const;
double constraintClampGain(const Params ¶ms, double gain) const;
utils::Duration filterExposure(utils::Duration exposureValue);
utils::Duration filteredExposure_;
mutable bool luxWarningEnabled_;
- Pwl relativeLuminanceTarget_;
uint64_t frameCount_;
std::vector<AgcConstraint> additionalConstraints_;
+
+ Pwl relativeLuminanceTarget_;
std::map<int32_t, std::vector<AgcConstraint>> constraintModes_;
std::map<int32_t, ExposureModeHelper> exposureModeHelpers_;
};
Move the tuning file parsing out of `AgcMeanLuminance`, and provide the configuration via the constructor. Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> --- src/ipa/libipa/agc.cpp | 257 +++++++++++++++++++++++++- src/ipa/libipa/agc_mean_luminance.cpp | 246 +----------------------- src/ipa/libipa/agc_mean_luminance.h | 22 +-- 3 files changed, 273 insertions(+), 252 deletions(-)