From patchwork Fri Jul 3 15:38:19 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: 27196 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 C39A6C3323 for ; Fri, 3 Jul 2026 15:38:46 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 482976601D; Fri, 3 Jul 2026 17:38:45 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="fpNMW/nT"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 99FD765FC5 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 8BCBADF3 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=GXzn8M8jH7x9pvaQEZpWK5TfAICmqncbl1I+BrEZruM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=fpNMW/nTxKapBxY4Gh0KFpxjsFV7i+hBrpnKu7kn1fPZ6FhF/54pZlFmSkwYpxWHB e2lz2vNKvDdLTDFcC6L1ca0ypjqf2q93anCpLOted2plimQ5BTKTxoxZv8KrBNB5zK NTQKYiWPpQfOw7uglIfVZctYISiDHM//PHa/hpUw= From: =?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= To: libcamera-devel@lists.libcamera.org Subject: [RFC PATCH v1 17/17] ipa: simple: agc: Use `AgcMeanLuminance` if sensor helper is available Date: Fri, 3 Jul 2026 17:38:19 +0200 Message-ID: <20260703153819.1088752-18-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" Signed-off-by: Barnabás Pőcze --- src/ipa/simple/algorithms/agc.cpp | 153 +++++++++++++++++++++++++----- src/ipa/simple/algorithms/agc.h | 9 +- src/ipa/simple/ipa_context.h | 4 + 3 files changed, 143 insertions(+), 23 deletions(-) diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp index db4f63eba5..7d035618e8 100644 --- a/src/ipa/simple/algorithms/agc.cpp +++ b/src/ipa/simple/algorithms/agc.cpp @@ -7,7 +7,12 @@ #include "agc.h" +#include + #include +#include + +#include namespace libcamera { @@ -15,38 +20,119 @@ LOG_DEFINE_CATEGORY(IPASoftExposure) namespace ipa::soft::algorithms { -int Agc::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData) +namespace { + +class AgcTraits : public AgcMeanLuminance::Traits { - return agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, { +public: + AgcTraits(const SwIspStats &stats) + : stats_(stats) + { + } + + double estimateLuminance(double gain) const override + { + double sum = 0; + double count = 0; + + for (const auto &[i, cnt] : utils::enumerate(stats_.yHistogram)) { + sum += std::min(1.0, gain * i / stats_.yHistogram.size()) * cnt; + count += cnt; + } + + return sum / count; + } + +private: + const SwIspStats &stats_; +}; + +} + +int Agc::init(IPAContext &context, const ValueNode &tuningData) +{ + const AgcAlgorithm::ConfigurationParams config = { .sensor = context.camHelper.get(), .sensorInfo = context.sensorInfo, .sensorControls = context.sensorControls, .ctrlMap = context.ctrlMap, .autoAllowed = true, - }); + }; + + if (config.sensor) + agc_.emplace(); + else + agc_.emplace(); + + return std::visit(utils::overloaded { + [&](AgcSimpleAlgorithm &impl) { + return impl.configure(context.configuration.agc.simple, + context.activeState.agc.simple, + config); + }, + [&](AgcMeanLuminanceAlgorithm &impl) { + int ret = impl.init(tuningData); + if (ret) + return ret; + + return impl.configure(context.configuration.agc.ml, + context.activeState.agc.ml, + config); + }, + }, agc_); } int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo) { - return agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, { + const AgcAlgorithm::ConfigurationParams config = { .sensor = context.camHelper.get(), .sensorInfo = context.sensorInfo, .sensorControls = context.sensorControls, .ctrlMap = context.ctrlMap, - .autoAllowed = true, // \todo not if raw? - }); + .autoAllowed = true, // \todo if not raw? + }; + + return std::visit(utils::overloaded { + [&](AgcSimpleAlgorithm &impl) { + return impl.configure(context.configuration.agc.simple, + context.activeState.agc.simple, + config); + }, + [&](AgcMeanLuminanceAlgorithm &impl) { + return impl.configure(context.configuration.agc.ml, + context.activeState.agc.ml, + config); + }, + }, agc_); } 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); + std::visit(utils::overloaded { + [&](AgcSimpleAlgorithm &impl) { + impl.queueRequest(context.configuration.agc.simple, + context.activeState.agc.simple, + frameContext.agc.simple, controls); + }, + [&](AgcMeanLuminanceAlgorithm &impl) { + impl.queueRequest(context.configuration.agc.ml, + context.activeState.agc.ml, + frameContext.agc.ml, controls); + }, + }, agc_); } 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); + std::visit(utils::overloaded { + [&](AgcSimpleAlgorithm &impl) { + impl.prepare(context.activeState.agc.simple, frameContext.agc.simple); + }, + [&](AgcMeanLuminanceAlgorithm &impl) { + impl.prepare(context.activeState.agc.ml, frameContext.agc.ml); + }, + }, agc_); } void Agc::process(IPAContext &context, @@ -55,20 +141,43 @@ void Agc::process(IPAContext &context, const SwIspStats *stats, ControlList &metadata) { - 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); - } + std::visit(utils::overloaded { + [&](AgcSimpleAlgorithm &impl) { + if (stats->valid) { + impl.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 { + impl.process(context.configuration.agc.simple, context.activeState.agc.simple, + frameContext.agc.simple, {}, metadata); + } + + frameContext.agc.exposure = frameContext.agc.simple.exposure; + frameContext.agc.gain = frameContext.agc.simple.gain; + }, + [&](AgcMeanLuminanceAlgorithm &impl) { + if (stats->valid) { + Histogram hist(stats->yHistogram); + + impl.process(context.configuration.agc.ml, context.activeState.agc.ml, frameContext.agc.ml, {{ + .traits = AgcTraits(*stats), + .hist = hist, + .exposure = uint32_t(frameContext.sensor.exposure), + .gain = frameContext.sensor.gain, + }}, metadata); + + } else { + impl.process(context.configuration.agc.ml, context.activeState.agc.ml, + frameContext.agc.ml, {}, metadata); + } - frameContext.agc.exposure = frameContext.agc.simple.exposure; - frameContext.agc.gain = frameContext.agc.simple.gain; + frameContext.agc.exposure = frameContext.agc.ml.exposure; + frameContext.agc.gain = frameContext.agc.ml.gain; + }, + }, agc_); } REGISTER_IPA_ALGORITHM(Agc, "Agc") diff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h index 869817fc95..5dac17ab1d 100644 --- a/src/ipa/simple/algorithms/agc.h +++ b/src/ipa/simple/algorithms/agc.h @@ -7,6 +7,10 @@ #pragma once +#include + +#include + #include "algorithm.h" #include "agc_simple.h" @@ -30,7 +34,10 @@ public: ControlList &metadata) override; private: - AgcSimpleAlgorithm agc_; + std::variant< + AgcSimpleAlgorithm, + AgcMeanLuminanceAlgorithm + > agc_; }; } /* namespace ipa::soft::algorithms */ diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h index 36e1d8bba8..b4a11b04cd 100644 --- a/src/ipa/simple/ipa_context.h +++ b/src/ipa/simple/ipa_context.h @@ -15,6 +15,7 @@ #include "libcamera/internal/matrix.h" #include "libcamera/internal/vector.h" +#include #include #include @@ -29,6 +30,7 @@ namespace ipa::soft { struct IPASessionConfiguration { struct { AgcSimpleAlgorithm::Session simple; + AgcMeanLuminanceAlgorithm::Session ml; double again10, againMinStep; } agc; struct { @@ -39,6 +41,7 @@ struct IPASessionConfiguration { struct IPAActiveState { struct { AgcSimpleAlgorithm::ActiveState simple; + AgcMeanLuminanceAlgorithm::ActiveState ml; } agc; struct { @@ -67,6 +70,7 @@ struct IPAFrameContext : public FrameContext { struct { AgcSimpleAlgorithm::FrameContext simple; + AgcMeanLuminanceAlgorithm::FrameContext ml; int32_t exposure; double gain; } agc;