{"id":27196,"url":"https://patchwork.libcamera.org/api/1.1/patches/27196/?format=json","web_url":"https://patchwork.libcamera.org/patch/27196/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260703153819.1088752-18-barnabas.pocze@ideasonboard.com>","date":"2026-07-03T15:38:19","name":"[RFC,v1,17/17] ipa: simple: agc: Use `AgcMeanLuminance` if sensor helper is available","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"e61dd03363c819db8ff8146d93ecfd95e769f661","submitter":{"id":216,"url":"https://patchwork.libcamera.org/api/1.1/people/216/?format=json","name":"Barnabás Pőcze","email":"barnabas.pocze@ideasonboard.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/27196/mbox/","series":[{"id":6036,"url":"https://patchwork.libcamera.org/api/1.1/series/6036/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=6036","date":"2026-07-03T15:38:02","name":"ipa: libipa: agc rework","version":1,"mbox":"https://patchwork.libcamera.org/series/6036/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/27196/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/27196/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id C39A6C3323\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri,  3 Jul 2026 15:38:46 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 482976601D;\n\tFri,  3 Jul 2026 17:38:45 +0200 (CEST)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[IPv6:2001:4b98:dc2:55:216:3eff:fef7:d647])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 99FD765FC5\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Jul 2026 17:38:26 +0200 (CEST)","from pb-laptop.local (185.221.140.128.nat.pool.zt.hu\n\t[185.221.140.128])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8BCBADF3\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri,  3 Jul 2026 17:37:40 +0200 (CEST)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"fpNMW/nT\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783093060;\n\tbh=GXzn8M8jH7x9pvaQEZpWK5TfAICmqncbl1I+BrEZruM=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=fpNMW/nTxKapBxY4Gh0KFpxjsFV7i+hBrpnKu7kn1fPZ6FhF/54pZlFmSkwYpxWHB\n\te2lz2vNKvDdLTDFcC6L1ca0ypjqf2q93anCpLOted2plimQ5BTKTxoxZv8KrBNB5zK\n\tNTQKYiWPpQfOw7uglIfVZctYISiDHM//PHa/hpUw=","From":"=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>","To":"libcamera-devel@lists.libcamera.org","Subject":"[RFC PATCH v1 17/17] ipa: simple: agc: Use `AgcMeanLuminance` if\n\tsensor 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","Content-Type":"text/plain; charset=UTF-8","Content-Transfer-Encoding":"8bit","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"Signed-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n src/ipa/simple/algorithms/agc.cpp | 153 +++++++++++++++++++++++++-----\n src/ipa/simple/algorithms/agc.h   |   9 +-\n src/ipa/simple/ipa_context.h      |   4 +\n 3 files changed, 143 insertions(+), 23 deletions(-)","diff":"diff --git a/src/ipa/simple/algorithms/agc.cpp b/src/ipa/simple/algorithms/agc.cpp\nindex db4f63eba5..7d035618e8 100644\n--- a/src/ipa/simple/algorithms/agc.cpp\n+++ b/src/ipa/simple/algorithms/agc.cpp\n@@ -7,7 +7,12 @@\n \n #include \"agc.h\"\n \n+#include <variant>\n+\n #include <libcamera/base/log.h>\n+#include <libcamera/base/utils.h>\n+\n+#include <libipa/histogram.h>\n \n namespace libcamera {\n \n@@ -15,38 +20,119 @@ LOG_DEFINE_CATEGORY(IPASoftExposure)\n \n namespace ipa::soft::algorithms {\n \n-int Agc::init(IPAContext &context, [[maybe_unused]] const ValueNode &tuningData)\n+namespace {\n+\n+class AgcTraits : public AgcMeanLuminance::Traits\n {\n-\treturn agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, {\n+public:\n+\tAgcTraits(const SwIspStats &stats)\n+\t\t: stats_(stats)\n+\t{\n+\t}\n+\n+\tdouble estimateLuminance(double gain) const override\n+\t{\n+\t\tdouble sum = 0;\n+\t\tdouble count = 0;\n+\n+\t\tfor (const auto &[i, cnt] : utils::enumerate(stats_.yHistogram)) {\n+\t\t\tsum += std::min(1.0, gain * i / stats_.yHistogram.size()) * cnt;\n+\t\t\tcount += cnt;\n+\t\t}\n+\n+\t\treturn sum / count;\n+\t}\n+\n+private:\n+\tconst SwIspStats &stats_;\n+};\n+\n+}\n+\n+int Agc::init(IPAContext &context, const ValueNode &tuningData)\n+{\n+\tconst AgcAlgorithm::ConfigurationParams config = {\n \t\t.sensor = context.camHelper.get(),\n \t\t.sensorInfo = context.sensorInfo,\n \t\t.sensorControls = context.sensorControls,\n \t\t.ctrlMap = context.ctrlMap,\n \t\t.autoAllowed = true,\n-\t});\n+\t};\n+\n+\tif (config.sensor)\n+\t\tagc_.emplace<AgcMeanLuminanceAlgorithm>();\n+\telse\n+\t\tagc_.emplace<AgcSimpleAlgorithm>();\n+\n+\treturn std::visit(utils::overloaded {\n+\t\t[&](AgcSimpleAlgorithm &impl) {\n+\t\t\treturn impl.configure(context.configuration.agc.simple,\n+\t\t\t\t\t      context.activeState.agc.simple,\n+\t\t\t\t\t      config);\n+\t\t},\n+\t\t[&](AgcMeanLuminanceAlgorithm &impl) {\n+\t\t\tint ret = impl.init(tuningData);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\n+\t\t\treturn impl.configure(context.configuration.agc.ml,\n+\t\t\t\t\t      context.activeState.agc.ml,\n+\t\t\t\t\t      config);\n+\t\t},\n+\t}, agc_);\n }\n \n int Agc::configure(IPAContext &context, [[maybe_unused]] const IPAConfigInfo &configInfo)\n {\n-\treturn agc_.configure(context.configuration.agc.simple, context.activeState.agc.simple, {\n+\tconst AgcAlgorithm::ConfigurationParams config = {\n \t\t.sensor = context.camHelper.get(),\n \t\t.sensorInfo = context.sensorInfo,\n \t\t.sensorControls = context.sensorControls,\n \t\t.ctrlMap = context.ctrlMap,\n-\t\t.autoAllowed = true, // \\todo not if raw?\n-\t});\n+\t\t.autoAllowed = true, // \\todo if not raw?\n+\t};\n+\n+\treturn std::visit(utils::overloaded {\n+\t\t[&](AgcSimpleAlgorithm &impl) {\n+\t\t\treturn impl.configure(context.configuration.agc.simple,\n+\t\t\t\t\t      context.activeState.agc.simple,\n+\t\t\t\t\t      config);\n+\t\t},\n+\t\t[&](AgcMeanLuminanceAlgorithm &impl) {\n+\t\t\treturn impl.configure(context.configuration.agc.ml,\n+\t\t\t\t\t      context.activeState.agc.ml,\n+\t\t\t\t\t      config);\n+\t\t},\n+\t}, agc_);\n }\n \n void Agc::queueRequest(IPAContext &context, [[maybe_unused]] const uint32_t frame, IPAFrameContext &frameContext, const ControlList &controls)\n {\n-\tagc_.queueRequest(context.configuration.agc.simple, context.activeState.agc.simple,\n-\t\t\t  frameContext.agc.simple, controls);\n+\tstd::visit(utils::overloaded {\n+\t\t[&](AgcSimpleAlgorithm &impl) {\n+\t\t\timpl.queueRequest(context.configuration.agc.simple,\n+\t\t\t\t\t  context.activeState.agc.simple,\n+\t\t\t\t\t  frameContext.agc.simple, controls);\n+\t\t},\n+\t\t[&](AgcMeanLuminanceAlgorithm &impl) {\n+\t\t\timpl.queueRequest(context.configuration.agc.ml,\n+\t\t\t\t\t  context.activeState.agc.ml,\n+\t\t\t\t\t  frameContext.agc.ml, controls);\n+\t\t},\n+\t}, agc_);\n }\n \n void Agc::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \t\t  IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)\n {\n-\tagc_.prepare(context.activeState.agc.simple, frameContext.agc.simple);\n+\tstd::visit(utils::overloaded {\n+\t\t[&](AgcSimpleAlgorithm &impl) {\n+\t\t\timpl.prepare(context.activeState.agc.simple, frameContext.agc.simple);\n+\t\t},\n+\t\t[&](AgcMeanLuminanceAlgorithm &impl) {\n+\t\t\timpl.prepare(context.activeState.agc.ml, frameContext.agc.ml);\n+\t\t},\n+\t}, agc_);\n }\n \n void Agc::process(IPAContext &context,\n@@ -55,20 +141,43 @@ void Agc::process(IPAContext &context,\n \t\t  const SwIspStats *stats,\n \t\t  ControlList &metadata)\n {\n-\tif (stats->valid) {\n-\t\tagc_.process(context.configuration.agc.simple, context.activeState.agc.simple, frameContext.agc.simple, {{\n-\t\t\t.exposure = frameContext.sensor.exposure,\n-\t\t\t.gain = frameContext.sensor.gain,\n-\t\t\t.stats = *stats,\n-\t\t\t.blackLevel = context.activeState.blc.level,\n-\t\t}}, metadata);\n-\t} else {\n-\t\tagc_.process(context.configuration.agc.simple, context.activeState.agc.simple,\n-\t\t\t     frameContext.agc.simple, {}, metadata);\n-\t}\n+\tstd::visit(utils::overloaded {\n+\t\t[&](AgcSimpleAlgorithm &impl) {\n+\t\t\tif (stats->valid) {\n+\t\t\t\timpl.process(context.configuration.agc.simple, context.activeState.agc.simple, frameContext.agc.simple, {{\n+\t\t\t\t\t.exposure = frameContext.sensor.exposure,\n+\t\t\t\t\t.gain = frameContext.sensor.gain,\n+\t\t\t\t\t.stats = *stats,\n+\t\t\t\t\t.blackLevel = context.activeState.blc.level,\n+\t\t\t\t}}, metadata);\n+\t\t\t} else {\n+\t\t\t\timpl.process(context.configuration.agc.simple, context.activeState.agc.simple,\n+\t\t\t\t\t     frameContext.agc.simple, {}, metadata);\n+\t\t\t}\n+\n+\t\t\tframeContext.agc.exposure = frameContext.agc.simple.exposure;\n+\t\t\tframeContext.agc.gain = frameContext.agc.simple.gain;\n+\t\t},\n+\t\t[&](AgcMeanLuminanceAlgorithm &impl) {\n+\t\t\tif (stats->valid) {\n+\t\t\t\tHistogram hist(stats->yHistogram);\n+\n+\t\t\t\timpl.process(context.configuration.agc.ml, context.activeState.agc.ml, frameContext.agc.ml, {{\n+\t\t\t\t\t.traits = AgcTraits(*stats),\n+\t\t\t\t\t.hist = hist,\n+\t\t\t\t\t.exposure = uint32_t(frameContext.sensor.exposure),\n+\t\t\t\t\t.gain = frameContext.sensor.gain,\n+\t\t\t\t}}, metadata);\n+\n+\t\t\t} else {\n+\t\t\t\timpl.process(context.configuration.agc.ml, context.activeState.agc.ml,\n+\t\t\t\t\t     frameContext.agc.ml, {}, metadata);\n+\t\t\t}\n \n-\tframeContext.agc.exposure = frameContext.agc.simple.exposure;\n-\tframeContext.agc.gain = frameContext.agc.simple.gain;\n+\t\t\tframeContext.agc.exposure = frameContext.agc.ml.exposure;\n+\t\t\tframeContext.agc.gain = frameContext.agc.ml.gain;\n+\t\t},\n+\t}, agc_);\n }\n \n REGISTER_IPA_ALGORITHM(Agc, \"Agc\")\ndiff --git a/src/ipa/simple/algorithms/agc.h b/src/ipa/simple/algorithms/agc.h\nindex 869817fc95..5dac17ab1d 100644\n--- a/src/ipa/simple/algorithms/agc.h\n+++ b/src/ipa/simple/algorithms/agc.h\n@@ -7,6 +7,10 @@\n \n #pragma once\n \n+#include <variant>\n+\n+#include <libipa/agc_mean_luminance.h>\n+\n #include \"algorithm.h\"\n #include \"agc_simple.h\"\n \n@@ -30,7 +34,10 @@ public:\n \t\t     ControlList &metadata) override;\n \n private:\n-\tAgcSimpleAlgorithm agc_;\n+\tstd::variant<\n+\t\tAgcSimpleAlgorithm,\n+\t\tAgcMeanLuminanceAlgorithm\n+\t> agc_;\n };\n \n } /* namespace ipa::soft::algorithms */\ndiff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h\nindex 36e1d8bba8..b4a11b04cd 100644\n--- a/src/ipa/simple/ipa_context.h\n+++ b/src/ipa/simple/ipa_context.h\n@@ -15,6 +15,7 @@\n #include \"libcamera/internal/matrix.h\"\n #include \"libcamera/internal/vector.h\"\n \n+#include <libipa/agc_mean_luminance.h>\n #include <libipa/camera_sensor_helper.h>\n #include <libipa/fc_queue.h>\n \n@@ -29,6 +30,7 @@ namespace ipa::soft {\n struct IPASessionConfiguration {\n \tstruct {\n \t\tAgcSimpleAlgorithm::Session simple;\n+\t\tAgcMeanLuminanceAlgorithm::Session ml;\n \t\tdouble again10, againMinStep;\n \t} agc;\n \tstruct {\n@@ -39,6 +41,7 @@ struct IPASessionConfiguration {\n struct IPAActiveState {\n \tstruct {\n \t\tAgcSimpleAlgorithm::ActiveState simple;\n+\t\tAgcMeanLuminanceAlgorithm::ActiveState ml;\n \t} agc;\n \n \tstruct {\n@@ -67,6 +70,7 @@ struct IPAFrameContext : public FrameContext {\n \n \tstruct {\n \t\tAgcSimpleAlgorithm::FrameContext simple;\n+\t\tAgcMeanLuminanceAlgorithm::FrameContext ml;\n \t\tint32_t exposure;\n \t\tdouble gain;\n \t} agc;\n","prefixes":["RFC","v1","17/17"]}