From patchwork Fri Jul 3 15:38:18 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= X-Patchwork-Id: 27194 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id C1C17C3303 for ; Fri, 3 Jul 2026 15:38:45 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1583D66019; Fri, 3 Jul 2026 17:38:43 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="BHLtqs7Y"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 68EB965FED for ; Fri, 3 Jul 2026 17:38:26 +0200 (CEST) Received: from pb-laptop.local (185.221.140.128.nat.pool.zt.hu [185.221.140.128]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id 50F671121 for ; Fri, 3 Jul 2026 17:37:40 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1783093060; bh=r4esyH1S/bN/Om3T3n+FhiOHUVvyWbjje7vOg/227eg=; h=From:To:Subject:Date:In-Reply-To:References:From; b=BHLtqs7Y7rBPHSBg1+RfzwQxfTXTnWs5NAdzsyKPy8bmqCxgzhVqlZxZIjnjqiTZW vIs7hT5bzW0Tn9ErR0LYJjujOY7HYugxL7ZjrZrs7nLHvszVXDgZJLRyWkc8Rw2KBw pX0C9IgUP3+5Ynm/6GpOY6AsSMu/iLaTII6flg0Q= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [RFC PATCH v1 16/17] ipa: simple: agc: Port to `AgcAlgorithm` Date: Fri, 3 Jul 2026 17:38:18 +0200 Message-ID: <20260703153819.1088752-17-barnabas.pocze@ideasonboard.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260703153819.1088752-1-barnabas.pocze@ideasonboard.com> References: <20260703153819.1088752-1-barnabas.pocze@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Move the agc algorithm into a separate class and use `AgcAlgorithm`. Signed-off-by: Barnabás Pőcze --- src/ipa/simple/agc_simple.cpp | 197 +++++++++++++++++++++++++++++ src/ipa/simple/agc_simple.h | 59 +++++++++ src/ipa/simple/algorithms/agc.cpp | 200 ++++++------------------------ src/ipa/simple/algorithms/agc.h | 9 +- src/ipa/simple/ipa_context.h | 16 +-- src/ipa/simple/meson.build | 1 + src/ipa/simple/soft_simple.cpp | 61 ++------- 7 files changed, 322 insertions(+), 221 deletions(-) create mode 100644 src/ipa/simple/agc_simple.cpp create mode 100644 src/ipa/simple/agc_simple.h diff --git a/src/ipa/simple/agc_simple.cpp b/src/ipa/simple/agc_simple.cpp new file mode 100644 index 0000000000..0734600105 --- /dev/null +++ b/src/ipa/simple/agc_simple.cpp @@ -0,0 +1,197 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Red Hat Inc. + * + * Exposure and gain + */ + +#include "agc_simple.h" + +#include +#include + +#include + +#include + +namespace libcamera { + +namespace ipa::soft { + +LOG_DEFINE_CATEGORY(IPASoftAgcSimple) + + +/* + * The number of bins to use for the optimal exposure calculations. + */ +static constexpr unsigned int kExposureBinsCount = 5; + +/* + * The exposure is optimal when the mean sample value of the histogram is + * in the middle of the range. + */ +static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; + +/* + * This implements the hysteresis for the exposure adjustment. + * It is small enough to have the exposure close to the optimal, and is big + * enough to prevent the exposure from wobbling around the optimal value. + */ +static constexpr float kExposureSatisfactory = 0.2; + +/* + * Proportional gain for exposure/gain adjustment. Maps the MSV error to a + * multiplicative correction factor: + * + * factor = 1.0 + kExpProportionalGain * error + * + * With kExpProportionalGain = 0.04: + * - max error ~2.5 -> factor 1.10 (~10% step, same as before) + * - error 1.0 -> factor 1.04 (~4% step) + * - error 0.3 -> factor 1.012 (~1.2% step) + * + * This replaces the fixed 10% bang-bang step with a proportional correction + * that converges smoothly and avoids overshooting near the target. + */ +static constexpr float kExpProportionalGain = 0.04; + +/* + * Maximum multiplicative step per frame, to bound the correction when the + * scene changes dramatically. + */ +static constexpr float kExpMaxStep = 0.15; + +void AgcSimpleAlgorithm::updateExposure(const Session &session, ActiveState &state, FrameContext &frameContext, + const ProcessParams ¶ms, double exposureMSV) +{ + double error = kExposureOptimal - exposureMSV; + if (std::abs(error) <= kExposureSatisfactory) + return; + + utils::Duration exposureDuration = params.exposure * session.lineDuration; + int32_t exposure = params.exposure; + double again = params.gain; + + /* + * Compute a proportional correction factor. The sign of the error + * determines the direction: positive error means too dark (increase), + * negative means too bright (decrease). + */ + float step = std::clamp(static_cast(error) * kExpProportionalGain, + -kExpMaxStep, kExpMaxStep); + float factor = 1.0f + step; + + const auto limits = AgcAlgorithm::calculateLimits(session, frameContext); + + if (factor > 1.0f) { + /* Scene too dark: increase exposure first, then gain. */ + if (exposureDuration < limits.exposure.second) { + int32_t next = static_cast(exposure * factor); + exposure = std::max(next, exposure + 1); + } else { + double next = again * factor; + if (next - again < session.gainMinStep) + again += session.gainMinStep; + else + again = next; + } + } else { + /* Scene too bright: decrease gain first, then exposure. */ + if (again > std::max(session.gain10, limits.gain.first)) { + double next = again * factor; + if (again - next < session.gainMinStep) + again -= session.gainMinStep; + else + again = next; + } else { + int32_t next = static_cast(exposure * factor); + exposure = std::min(next, exposure - 1); + } + } + + exposureDuration = std::clamp( + exposure * session.lineDuration, + limits.exposure.first, limits.exposure.second); + exposure = exposureDuration / session.lineDuration; + again = std::clamp(again, limits.gain.first, limits.gain.second); + + state.automatic.exposure = exposure; + state.automatic.gain = again; + + LOG(IPASoftAgcSimple, Debug) + << "exposureMSV " << exposureMSV + << " error " << error << " factor " << factor + << " exp " << exposure << " again " << again; +} + +int AgcSimpleAlgorithm::configure(Session &session, ActiveState &state, const ConfigurationParams &config) +{ + int ret = AgcAlgorithm::configure(session, state, config); + if (ret) + return ret; + + const ControlInfo &v4l2Gain = config.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second; + auto defGain = v4l2Gain.def().get(); + + if (config.sensor) { + session.gain10 = std::max(session.minAnalogueGain, 1.0); + session.gainMinStep = (session.maxAnalogueGain - session.minAnalogueGain) / 100.0; + } else { + session.gain10 = defGain; + session.gainMinStep = 1.0; + } + + return 0; +} + +void AgcSimpleAlgorithm::process(const Session &session, ActiveState &state, + FrameContext &frameContext, std::optional &¶ms, + ControlList &metadata) +{ + utils::Duration newExposureTime = {}; + + if (params) { + /* + * Calculate Mean Sample Value (MSV) according to formula from: + * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf + */ + const auto &histogram = params->stats.yHistogram; + const unsigned int blackLevelHistIdx = params->blackLevel / (256 / SwIspStats::kYHistogramSize); + const unsigned int histogramSize = + SwIspStats::kYHistogramSize - blackLevelHistIdx; + const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount; + const unsigned int yHistValsPerBinMod = + histogramSize / (histogramSize % kExposureBinsCount + 1); + int exposureBins[kExposureBinsCount] = {}; + unsigned int denom = 0; + unsigned int num = 0; + + if (yHistValsPerBin == 0) { + LOG(IPASoftAgcSimple, Debug) + << "Not adjusting exposure due to insufficient histogram data"; + return; + } + + for (unsigned int i = 0; i < histogramSize; i++) { + unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin; + exposureBins[idx] += histogram[blackLevelHistIdx + i]; + } + + for (unsigned int i = 0; i < kExposureBinsCount; i++) { + LOG(IPASoftAgcSimple, Debug) << i << ": " << exposureBins[i]; + denom += exposureBins[i]; + num += exposureBins[i] * (i + 1); + } + + float exposureMSV = (denom == 0 ? 0 : static_cast(num) / denom); + updateExposure(session, state, frameContext, *params, exposureMSV); + newExposureTime = state.automatic.exposure * session.lineDuration; + } + + AgcAlgorithm::process(session, frameContext, newExposureTime, metadata); +} + + +} /* namespace ipa::soft::algorithms */ + +} /* namespace libcamera */ diff --git a/src/ipa/simple/agc_simple.h b/src/ipa/simple/agc_simple.h new file mode 100644 index 0000000000..28e414a291 --- /dev/null +++ b/src/ipa/simple/agc_simple.h @@ -0,0 +1,59 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Red Hat Inc. + * + * Exposure and gain + */ + +#pragma once + +#include + +#include + +#include "libcamera/internal/software_isp/swisp_stats.h" + +namespace libcamera { + +namespace ipa::soft { + +class AgcSimpleAlgorithm : public AgcAlgorithm +{ +public: + struct Session : AgcAlgorithm::Session { + double gain10; + double gainMinStep; + }; + + struct ProcessParams { + int32_t exposure; + double gain; + const SwIspStats &stats; + unsigned int blackLevel; + }; + + int configure(Session &session, ActiveState &state, const ConfigurationParams &config); + + void prepare(ActiveState &state, FrameContext &frameContext) + { + return AgcAlgorithm::prepare(state, frameContext); + } + + void queueRequest(const Session &session, ActiveState &state, + FrameContext &frameContext, const ControlList &controls) + { + return AgcAlgorithm::queueRequest(session, state, frameContext, controls); + } + + void process(const Session &session, ActiveState &state, + FrameContext &frameContext, std::optional &¶ms, + ControlList &metadata); + +private: + void updateExposure(const Session &session, ActiveState &state, FrameContext &frameContext, + const ProcessParams ¶ms, double exposureMSV); +}; + +} /* namepsace ipa::soft */ + +} /* namespace libcamera */ diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index e9bcb2c032..db4f63eba5 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -7,124 +7,46 @@ #include "agc.h" -#include -#include -#include - #include -#include "control_ids.h" - namespace libcamera { LOG_DEFINE_CATEGORY(IPASoftExposure) namespace ipa::soft::algorithms { -/* - * The number of bins to use for the optimal exposure calculations. - */ -static constexpr unsigned int kExposureBinsCount = 5; - -/* - * The exposure is optimal when the mean sample value of the histogram is - * in the middle of the range. - */ -static constexpr float kExposureOptimal = kExposureBinsCount / 2.0; - -/* - * This implements the hysteresis for the exposure adjustment. - * It is small enough to have the exposure close to the optimal, and is big - * enough to prevent the exposure from wobbling around the optimal value. - */ -static constexpr float kExposureSatisfactory = 0.2; - -/* - * Proportional gain for exposure/gain adjustment. Maps the MSV error to a - * multiplicative correction factor: - * - * factor = 1.0 + kExpProportionalGain * error - * - * With kExpProportionalGain = 0.04: - * - max error ~2.5 -> factor 1.10 (~10% step, same as before) - * - error 1.0 -> factor 1.04 (~4% step) - * - error 0.3 -> factor 1.012 (~1.2% step) - * - * This replaces the fixed 10% bang-bang step with a proportional correction - * that converges smoothly and avoids overshooting near the target. - */ -static constexpr float kExpProportionalGain = 0.04; - -/* - * Maximum multiplicative step per frame, to bound the correction when the - * scene changes dramatically. - */ -static constexpr float kExpMaxStep = 0.15; - -Agc::Agc() +int Agc::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData) { + return agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, { + .sensor = context.camHelper.get(), + .sensorInfo = context.sensorInfo, + .sensorControls = context.sensorControls, + .ctrlMap = context.ctrlMap, + .autoAllowed = true, + }); } -void Agc::updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV) +int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { - int32_t exposure = frameContext.sensor.exposure; - double again = frameContext.sensor.gain; - - double error = kExposureOptimal - exposureMSV; - - if (std::abs(error) <= kExposureSatisfactory) - return; - - /* - * Compute a proportional correction factor. The sign of the error - * determines the direction: positive error means too dark (increase), - * negative means too bright (decrease). - */ - float step = std::clamp(static_cast(error) * kExpProportionalGain, - -kExpMaxStep, kExpMaxStep); - float factor = 1.0f + step; - - if (factor > 1.0f) { - /* Scene too dark: increase exposure first, then gain. */ - if (exposure < context.configuration.agc.exposureMax) { - int32_t next = static_cast(exposure * factor); - exposure = std::max(next, exposure + 1); - } else { - double next = again * factor; - if (next - again < context.configuration.agc.againMinStep) - again += context.configuration.agc.againMinStep; - else - again = next; - } - } else { - /* Scene too bright: decrease gain first, then exposure. */ - if (again > context.configuration.agc.again10) { - double next = again * factor; - if (again - next < context.configuration.agc.againMinStep) - again -= context.configuration.agc.againMinStep; - else - again = next; - } else { - int32_t next = static_cast(exposure * factor); - exposure = std::min(next, exposure - 1); - } - } - - exposure = std::clamp(exposure, context.configuration.agc.exposureMin, - context.configuration.agc.exposureMax); - again = std::clamp(again, context.configuration.agc.againMin, - context.configuration.agc.againMax); - - frameContext.agc.exposure = exposure; - frameContext.agc.gain = again; + return agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, { + .sensor = context.camHelper.get(), + .sensorInfo = context.sensorInfo, + .sensorControls = context.sensorControls, + .ctrlMap = context.ctrlMap, + .autoAllowed = true, // \todo not if raw? + }); +} - context.activeState.agc.exposure = exposure; - context.activeState.agc.again = again; +void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, const ControlList &controls) +{ + agc_.queueRequest(context.configuration.agc.simple, context.activeState.agc.simple, + frameContext.agc.simple, controls); +} - LOG(IPASoftExposure, Debug) - << "exposureMSV " << exposureMSV - << " error " << error << " factor " << factor - << " exp " << exposure << " again " << again; +void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame, + IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params) +{ + agc_.prepare(context.activeState.agc.simple, frameContext.agc.simple); } void Agc::process(IPAContext &context, @@ -133,66 +55,20 @@ 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()); - 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; - } - - /* - * Calculate Mean Sample Value (MSV) according to formula from: - * https://www.araa.asn.au/acra/acra2007/papers/paper84final.pdf - */ - const auto &histogram = stats->yHistogram; - const unsigned int blackLevelHistIdx = - context.activeState.blc.level / (256 / SwIspStats::kYHistogramSize); - const unsigned int histogramSize = - SwIspStats::kYHistogramSize - blackLevelHistIdx; - const unsigned int yHistValsPerBin = histogramSize / kExposureBinsCount; - const unsigned int yHistValsPerBinMod = - histogramSize / (histogramSize % kExposureBinsCount + 1); - int exposureBins[kExposureBinsCount] = {}; - unsigned int denom = 0; - unsigned int num = 0; - - if (yHistValsPerBin == 0) { - LOG(IPASoftExposure, Debug) - << "Not adjusting exposure due to insufficient histogram data"; - return; - } - - for (unsigned int i = 0; i < histogramSize; i++) { - unsigned int idx = (i - (i / yHistValsPerBinMod)) / yHistValsPerBin; - exposureBins[idx] += histogram[blackLevelHistIdx + i]; - } - - for (unsigned int i = 0; i < kExposureBinsCount; i++) { - LOG(IPASoftExposure, Debug) << i << ": " << exposureBins[i]; - denom += exposureBins[i]; - num += exposureBins[i] * (i + 1); + if (stats->valid) { + agc_.process(context.configuration.agc.simple, context.activeState.agc.simple, frameContext.agc.simple, {{ + .exposure = frameContext.sensor.exposure, + .gain = frameContext.sensor.gain, + .stats = *stats, + .blackLevel = context.activeState.blc.level, + }}, metadata); + } else { + agc_.process(context.configuration.agc.simple, context.activeState.agc.simple, + frameContext.agc.simple, {}, metadata); } - float exposureMSV = (denom == 0 ? 0 : static_cast(num) / denom); - updateExposure(context, frameContext, exposureMSV); + frameContext.agc.exposure = frameContext.agc.simple.exposure; + frameContext.agc.gain = frameContext.agc.simple.gain; } REGISTER_IPA_ALGORITHM(Agc, "Agc") diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h index 112d9f5a19..869817fc95 100644 --- a/src/ipa/simple/algorithms/agc.h +++ b/src/ipa/simple/algorithms/agc.h @@ -8,6 +8,7 @@ #pragma once #include "algorithm.h" +#include "agc_simple.h" namespace libcamera { @@ -16,16 +17,20 @@ namespace ipa::soft::algorithms { class Agc : public Algorithm { public: - Agc(); + Agc() = default; ~Agc() = default; + 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: - void updateExposure(IPAContext &context, IPAFrameContext &frameContext, double exposureMSV); + AgcSimpleAlgorithm agc_; }; } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index d35fb1e91d..36e1d8bba8 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -7,7 +7,6 @@ #pragma once -#include #include #include @@ -16,19 +15,21 @@ #include "libcamera/internal/matrix.h" #include "libcamera/internal/vector.h" +#include #include #include "core_ipa_interface.h" +#include "agc_simple.h" + namespace libcamera { namespace ipa::soft { struct IPASessionConfiguration { struct { - int32_t exposureMin, exposureMax; - double againMin, againMax, again10, againMinStep; - utils::Duration lineDuration; + AgcSimpleAlgorithm::Session simple; + double again10, againMinStep; } agc; struct { std::optional level; @@ -37,9 +38,7 @@ struct IPASessionConfiguration { struct IPAActiveState { struct { - int32_t exposure; - double again; - bool valid; + AgcSimpleAlgorithm::ActiveState simple; } agc; struct { @@ -67,6 +66,7 @@ struct IPAFrameContext : public FrameContext { Matrix ccm; struct { + AgcSimpleAlgorithm::FrameContext simple; int32_t exposure; double gain; } agc; @@ -90,10 +90,12 @@ struct IPAContext { } IPACameraSensorInfo sensorInfo; + ControlInfoMap sensorControls; IPASessionConfiguration configuration; IPAActiveState activeState; FCQueue frameContexts; ControlInfoMap::Map ctrlMap; + std::unique_ptr camHelper; bool ccmEnabled = false; }; diff --git a/src/ipa/simple/meson.build b/src/ipa/simple/meson.build index 2f9f15f4aa..3033e91763 100644 --- a/src/ipa/simple/meson.build +++ b/src/ipa/simple/meson.build @@ -6,6 +6,7 @@ subdir('data') ipa_name = 'ipa_soft_simple' soft_simple_sources = files([ + 'agc_simple.cpp', 'ipa_context.cpp', 'soft_simple.cpp', ]) diff --git a/src/ipa/simple/soft_simple.cpp b/src/ipa/simple/soft_simple.cpp index 22702638bb..5c85b3986f 100644 --- a/src/ipa/simple/soft_simple.cpp +++ b/src/ipa/simple/soft_simple.cpp @@ -5,7 +5,6 @@ * Simple Software Image Processing Algorithm module */ -#include #include #include @@ -34,8 +33,6 @@ namespace libcamera { LOG_DEFINE_CATEGORY(IPASoft) -using namespace std::literals::chrono_literals; - namespace ipa::soft { /* Maximum number of frame contexts to be held */ @@ -76,8 +73,6 @@ private: DebayerParams *params_; SwIspStats *stats_; - std::unique_ptr camHelper_; - ControlInfoMap sensorInfoMap_; /* Local parameter storage */ struct IPAContext context_; @@ -99,14 +94,15 @@ int IPASoftSimple::init(const IPASettings &settings, ControlInfoMap *ipaControls, bool *ccmEnabled) { - camHelper_ = CameraSensorHelperFactoryBase::create(settings.sensorModel); - if (!camHelper_) { + context_.camHelper = CameraSensorHelperFactoryBase::create(settings.sensorModel); + if (!context_.camHelper) { LOG(IPASoft, Warning) << "Failed to create camera sensor helper for " << settings.sensorModel; } context_.sensorInfo = sensorInfo; + context_.sensorControls = sensorControls; /* Load the tuning data file */ File file(settings.configurationFile); @@ -201,38 +197,15 @@ int IPASoftSimple::init(const IPASettings &settings, int IPASoftSimple::configure(const IPAConfigInfo &configInfo) { - sensorInfoMap_ = configInfo.sensorControls; - - const ControlInfo &exposureInfo = sensorInfoMap_.find(V4L2_CID_EXPOSURE)->second; - const ControlInfo &gainInfo = sensorInfoMap_.find(V4L2_CID_ANALOGUE_GAIN)->second; + context_.sensorControls = configInfo.sensorControls; /* 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(); - context_.configuration.agc.exposureMax = exposureInfo.max().get(); - 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 againMax = gainInfo.max().get(); - int32_t againDef = gainInfo.def().get(); - - if (camHelper_) { - context_.configuration.agc.againMin = camHelper_->gain(againMin); - context_.configuration.agc.againMax = 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; - if (camHelper_->blackLevel().has_value()) { + if (context_.camHelper) { + if (context_.camHelper->blackLevel().has_value()) { /* * The black level from camHelper_ is a 16 bit value, software ISP * works with 8 bit pixel values, both regardless of the actual @@ -240,13 +213,8 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo) * by dividing the value from the helper by 256. */ context_.configuration.black.level = - camHelper_->blackLevel().value() / 256; + context_.camHelper->blackLevel().value() / 256; } - } 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()) { @@ -255,13 +223,6 @@ int IPASoftSimple::configure(const IPAConfigInfo &configInfo) 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 << ")"; - return 0; } @@ -311,17 +272,17 @@ void IPASoftSimple::processStats(const uint32_t frame, frameContext.sensor.exposure = sensorControls.get(V4L2_CID_EXPOSURE).get(); int32_t again = sensorControls.get(V4L2_CID_ANALOGUE_GAIN).get(); - frameContext.sensor.gain = camHelper_ ? camHelper_->gain(again) : again; + frameContext.sensor.gain = context_.camHelper ? context_.camHelper->gain(again) : again; ControlList metadata(controls::controls); for (const auto &algo : algorithms()) algo->process(context_, frame, frameContext, stats_, metadata); metadataReady.emit(frame, metadata); - ControlList ctrls(sensorInfoMap_); + ControlList ctrls(context_.sensorControls); - int32_t againNew = camHelper_ - ? camHelper_->gainCode(frameContext.agc.gain) + int32_t againNew = context_.camHelper + ? context_.camHelper->gainCode(frameContext.agc.gain) : static_cast(frameContext.agc.gain); ctrls.set(V4L2_CID_EXPOSURE, frameContext.agc.exposure); ctrls.set(V4L2_CID_ANALOGUE_GAIN, againNew);