@@ -13,30 +13,76 @@
#include <libipa/histogram.h>
-#include "control_ids.h"
-
namespace libcamera {
LOG_DEFINE_CATEGORY(IPASoftExposure)
namespace ipa::soft::algorithms {
+namespace {
+
+class AgcTraits : public AgcMeanLuminance::Traits
+{
+public:
+ AgcTraits(const Histogram &yHist)
+ : yHist_(yHist)
+ {
+ }
+
+ double estimateLuminance(double gain) const override
+ {
+ /*
+ * TODO: Improve by asking the weight of saturating and non-saturating
+ * bins directly from the histogram
+ */
+ double sum = 0;
+
+ for (size_t i = 0; i < yHist_.bins(); i++)
+ sum += std::min<double>(yHist_.bins(), i * gain) * yHist_[i];
+
+ return sum / yHist_.total() / yHist_.bins();
+ }
+
+private:
+ const Histogram &yHist_;
+};
+
+} /* namespace */
+
+int Agc::init(IPAContext &context, const ValueNode &tuningData)
+{
+ int ret = agc_.init(tuningData, context.camHelper.get());
+ if (ret)
+ return ret;
+
+ return agc_.configure(context.configuration.agc, context.activeState.agc, {
+ .sensorInfo = context.sensorInfo,
+ .sensorControls = context.sensorControls,
+ .ctrlMap = context.ctrlMap,
+ .autoAllowed = true,
+ });
+}
+
int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo)
{
- agc_.setLimits({
- .exposure = {
- context.configuration.agc.exposureMin,
- context.configuration.agc.exposureMax,
- },
- .gain = {
- context.configuration.agc.againMin,
- context.configuration.agc.againMax,
- },
- .gainMinStep = context.configuration.agc.againMinStep,
- .gain1 = context.configuration.agc.again10,
+ return agc_.configure(context.configuration.agc, context.activeState.agc, {
+ .sensorInfo = context.sensorInfo,
+ .sensorControls = context.sensorControls,
+ .ctrlMap = context.ctrlMap,
+ .autoAllowed = true,
});
+}
+
+void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext, const ControlList &controls)
+{
+ agc_.queueRequest(context.configuration.agc, context.activeState.agc, frameContext.agc, controls);
+}
- return 0;
+void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,
+ IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)
+{
+ agc_.prepare(context.activeState.agc, frameContext.agc);
}
void Agc::process(IPAContext &context,
@@ -45,49 +91,25 @@ void Agc::process(IPAContext &context,
const SwIspStats *stats,
ControlList &metadata)
{
- utils::Duration exposureTime =
- context.configuration.agc.lineDuration * frameContext.sensor.exposure;
- metadata.set(controls::ExposureTime, exposureTime.get<std::micro>());
- metadata.set(controls::AnalogueGain, frameContext.sensor.gain);
-
- if (!context.activeState.agc.valid) {
- /*
- * Init active-state from sensor values in case updateExposure()
- * does not run for the first frame.
- */
- context.activeState.agc.exposure = frameContext.sensor.exposure;
- context.activeState.agc.again = frameContext.sensor.gain;
- context.activeState.agc.valid = true;
- }
-
- if (!stats->valid) {
- /*
- * Use the new exposure and gain values calculated the last time
- * there were valid stats.
- */
- frameContext.agc.exposure = context.activeState.agc.exposure;
- frameContext.agc.gain = context.activeState.agc.again;
- return;
+ if (stats->valid) {
+ auto histogram = stats->yHistogram;
+
+ const unsigned int blackLevelHistIdx =
+ context.activeState.blc.level * std::size(histogram) / 256;
+ for (size_t i = 1; i < blackLevelHistIdx; i++)
+ histogram[0] += std::exchange(histogram[i], 0);
+
+ Histogram yHist(histogram);
+
+ agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {{
+ .traits = AgcTraits(yHist),
+ .yHist = yHist,
+ .exposure = frameContext.sensor.exposure,
+ .gain = frameContext.sensor.gain,
+ }}, metadata);
+ } else {
+ agc_.process(context.configuration.agc, context.activeState.agc, frameContext.agc, {}, metadata);
}
-
- auto histogram = stats->yHistogram;
- const unsigned int blackLevelHistIdx =
- context.activeState.blc.level * std::size(histogram) / 256;
-
- for (unsigned int i = 1; i < blackLevelHistIdx; i++)
- histogram[0] += std::exchange(histogram[i], 0);
-
- const auto &newEv = agc_.calculateNewEv({
- .yHist = { histogram },
- .exposure = frameContext.sensor.exposure,
- .gain = frameContext.sensor.gain,
- });
-
- frameContext.agc.exposure = newEv.exposure;
- frameContext.agc.gain = newEv.analogueGain;
-
- context.activeState.agc.exposure = frameContext.agc.exposure;
- context.activeState.agc.again = frameContext.agc.gain;
}
REGISTER_IPA_ALGORITHM(Agc, "Agc")
@@ -9,7 +9,7 @@
#include "algorithm.h"
-#include <libipa/agc_msv.h>
+#include <libipa/agc.h>
namespace libcamera {
@@ -18,15 +18,23 @@ namespace ipa::soft::algorithms {
class Agc : public Algorithm
{
public:
+ int init(IPAContext &context, const ValueNode &tuningData) override;
+
int configure(IPAContext &context, const IPAConfigInfo &configInfo) override;
+ void queueRequest(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext, const ControlList &controls) override;
+
+ void prepare(IPAContext &context, const uint32_t frame,
+ IPAFrameContext &frameContext, DebayerParams *params) override;
+
void process(IPAContext &context, const uint32_t frame,
IPAFrameContext &frameContext,
const SwIspStats *stats,
ControlList &metadata) override;
private:
- AgcMSV agc_;
+ AgcAlgorithm agc_;
};
} /* namespace ipa::soft::algorithms */
@@ -17,6 +17,7 @@
#include "libcamera/internal/matrix.h"
#include "libcamera/internal/vector.h"
+#include <libipa/agc.h>
#include <libipa/camera_sensor_helper.h>
#include <libipa/fc_queue.h>
@@ -27,10 +28,7 @@ namespace libcamera {
namespace ipa::soft {
struct IPASessionConfiguration {
- struct {
- uint32_t exposureMin, exposureMax;
- double againMin, againMax, again10, againMinStep;
- utils::Duration lineDuration;
+ struct Agc : agc::Session {
} agc;
struct {
std::optional<uint8_t> level;
@@ -38,10 +36,7 @@ struct IPASessionConfiguration {
};
struct IPAActiveState {
- struct {
- uint32_t exposure;
- double again;
- bool valid;
+ struct Agc : agc::ActiveState {
} agc;
struct {
@@ -68,9 +63,7 @@ struct IPAActiveState {
struct IPAFrameContext : public FrameContext {
Matrix<float, 3, 3> ccm;
- struct {
- uint32_t exposure;
- double gain;
+ struct Agc : agc::FrameContext {
} agc;
struct {
@@ -107,6 +107,7 @@ int IPASoftSimple::init(const IPASettings &settings,
}
context_.sensorInfo = sensorInfo;
+ context_.sensorControls = sensorControls;
/* Load the tuning data file */
File file(settings.configurationFile);
@@ -180,22 +181,6 @@ int IPASoftSimple::init(const IPASettings &settings,
ControlInfoMap::Map ctrlMap = context_.ctrlMap;
*ipaControls = ControlInfoMap(std::move(ctrlMap), controls::controls);
- /*
- * Check if the sensor driver supports the controls required by the
- * Soft IPA.
- * Don't save the min and max control values yet, as e.g. the limits
- * for V4L2_CID_EXPOSURE depend on the configured sensor resolution.
- */
- if (sensorControls.find(V4L2_CID_EXPOSURE) == sensorControls.end()) {
- LOG(IPASoft, Error) << "Don't have exposure control";
- return -EINVAL;
- }
-
- if (sensorControls.find(V4L2_CID_ANALOGUE_GAIN) == sensorControls.end()) {
- LOG(IPASoft, Error) << "Don't have gain control";
- return -EINVAL;
- }
-
return 0;
}
@@ -203,55 +188,17 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo, ControlInfoMap *ip
{
context_.sensorControls = configInfo.sensorControls;
- const ControlInfo &exposureInfo = context_.sensorControls.find(V4L2_CID_EXPOSURE)->second;
- const ControlInfo &gainInfo = context_.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;
-
/* Clear the IPA context before the streaming session. */
context_.configuration = {};
context_.activeState = {};
context_.frameContexts.clear();
- context_.configuration.agc.lineDuration =
- context_.sensorInfo.minLineLength * 1.0s / context_.sensorInfo.pixelRate;
- context_.configuration.agc.exposureMin = exposureInfo.min().get<int32_t>();
- context_.configuration.agc.exposureMax = exposureInfo.max().get<int32_t>();
- if (!context_.configuration.agc.exposureMin) {
- LOG(IPASoft, Warning) << "Minimum exposure is zero, that can't be linear";
- context_.configuration.agc.exposureMin = 1;
- }
-
- int32_t againMin = gainInfo.min().get<int32_t>();
- int32_t againMax = gainInfo.max().get<int32_t>();
- int32_t againDef = gainInfo.def().get<int32_t>();
-
- if (context_.camHelper) {
- context_.configuration.agc.againMin = context_.camHelper->gain(againMin);
- context_.configuration.agc.againMax = context_.camHelper->gain(againMax);
- context_.configuration.agc.again10 = std::max(context_.configuration.agc.againMin, 1.0);
- context_.configuration.agc.againMinStep =
- (context_.configuration.agc.againMax -
- context_.configuration.agc.againMin) /
- 100.0;
- } else {
- context_.configuration.agc.againMax = againMax;
- context_.configuration.agc.again10 = againDef;
- context_.configuration.agc.againMin = againMin;
- context_.configuration.agc.againMinStep = 1.0;
- }
-
for (const auto &algo : algorithms()) {
int ret = algo->configure(context_, configInfo);
if (ret)
return ret;
}
- LOG(IPASoft, Info)
- << "Exposure " << context_.configuration.agc.exposureMin << "-"
- << context_.configuration.agc.exposureMax
- << ", gain " << context_.configuration.agc.againMin << "-"
- << context_.configuration.agc.againMax
- << " (" << context_.configuration.agc.againMinStep << ")";
-
*ipaControls = { ControlInfoMap::Map(context_.ctrlMap), controls::controls };
return 0;
Use the `AgcAlgorithm` class to implement the agc algorithm. Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com> --- src/ipa/simple/algorithms/agc.cpp | 134 +++++++++++++++++------------- src/ipa/simple/algorithms/agc.h | 12 ++- src/ipa/simple/ipa_context.h | 15 +--- src/ipa/simple/soft_simple.cpp | 55 +----------- 4 files changed, 93 insertions(+), 123 deletions(-)