Patch Detail
Show a patch.
GET /api/patches/27190/?format=api
{ "id": 27190, "url": "https://patchwork.libcamera.org/api/patches/27190/?format=api", "web_url": "https://patchwork.libcamera.org/patch/27190/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/projects/1/?format=api", "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-11-barnabas.pocze@ideasonboard.com>", "date": "2026-07-03T15:38:12", "name": "[RFC,v1,10/17] ipa: libipa: Add `AgcMeanLuminance` wrapper", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "c1d5d329df0258334b407f1efd9495e6ffb748ee", "submitter": { "id": 216, "url": "https://patchwork.libcamera.org/api/people/216/?format=api", "name": "Barnabás Pőcze", "email": "barnabas.pocze@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/27190/mbox/", "series": [ { "id": 6036, "url": "https://patchwork.libcamera.org/api/series/6036/?format=api", "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/27190/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/27190/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 8D0DEC330A\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 3 Jul 2026 15:38:43 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1C4F56600C;\n\tFri, 3 Jul 2026 17:38:39 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id E08E565FDC\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 3 Jul 2026 17:38:24 +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 C81DD1121\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 3 Jul 2026 17:37:38 +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=\"AMTG1rjI\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783093058;\n\tbh=5O8ZU2Gcw9FKr8ilWmixdJ36NgReYWWAbkJRbpl3tIs=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=AMTG1rjIQV1FseWcBe1KGcVLAJaBxO48tJqCkKcE8h5mg30R9/mzZGEo/Jc591kux\n\tpaddmhH1YmOcJIxuwkFKDgthlSNVxe4+lacxt7l7mrSP2PtCW8xWODh+kePwEbyUGP\n\tIrRzDqks6LJaewo6L0VzpVgV5keRLoeh1RqxHFPQ=", "From": "=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Subject": "[RFC PATCH v1 10/17] ipa: libipa: Add `AgcMeanLuminance` wrapper", "Date": "Fri, 3 Jul 2026 17:38:12 +0200", "Message-ID": "<20260703153819.1088752-11-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": "Add an `AgcMeanLuminance` wrapper that can be used to easily implement\nthe `Algorithm` interface in an IPA module. This implementation is a\ncopy of the rkisp1 agc algorithm with minor adjustments.\n\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n src/ipa/libipa/agc_mean_luminance.cpp | 599 ++++++++++++++++++++++++++\n src/ipa/libipa/agc_mean_luminance.h | 96 +++++\n 2 files changed, 695 insertions(+)", "diff": "diff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp\nindex 1529d55864..951a4b0e02 100644\n--- a/src/ipa/libipa/agc_mean_luminance.cpp\n+++ b/src/ipa/libipa/agc_mean_luminance.cpp\n@@ -8,8 +8,11 @@\n #include \"agc_mean_luminance.h\"\n \n #include <algorithm>\n+#include <chrono>\n #include <cmath>\n \n+#include <linux/v4l2-controls.h>\n+\n #include <libcamera/base/log.h>\n #include <libcamera/base/utils.h>\n \n@@ -738,6 +741,602 @@ AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex,\n * their configure() function.\n */\n \n+/**\n+ * \\class AgcMeanLuminanceAlgorithm\n+ * \\brief AgcMeanLuminance wrapper for implementing the Algorithm interface\n+ *\n+ * \\todo DigitalGain, DigitalGainMode\n+ */\n+\n+/**\n+ * \\struct AgcMeanLuminanceAlgorithm::Session\n+ * \\brief Session configuration for AgcMeanLuminanceAlgorithm\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::minExposureTime\n+ * \\brief Minimum exposure time supported with the configured sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::maxExposureTime\n+ * \\brief Maximum exposure time supported with the configured sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::minAnalogueGain\n+ * \\brief Minimum analogue gain supported with the configured sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::maxAnalogueGain\n+ * \\brief Maximum analogue gain supported with the configured sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::minFrameDuration\n+ * \\brief Minimum frame duration supported with the configured sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::maxFrameDuration\n+ * \\brief Maximum frame duration supported with the configured sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::lineDuration\n+ * \\brief Line duration with the configured sensor and output size\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::sensor\n+ * \\brief Details of the sensor configuration\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::sensor.outputSize\n+ * \\brief Configured output size of the sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::Session::autoAllowed\n+ * \\brief Whether automatic controls are allowed\n+ */\n+\n+/**\n+ * \\struct AgcMeanLuminanceAlgorithm::ActiveState\n+ * \\brief Active state for AgcMeanLuminanceAlgorithm\n+ *\n+ * The \\a automatic variables track the latest values computed by algorithm\n+ * based on the latest processed statistics. All other variables track the\n+ * consolidated controls requested in queued requests.\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::manual\n+ * \\brief Manual exposure time and analog gain (set through requests)\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::manual.exposure\n+ * \\brief Manual exposure time expressed as a number of lines as set by the\n+ * ExposureTime control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::manual.gain\n+ * \\brief Manual analogue gain as set by the AnalogueGain control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::automatic\n+ * \\brief Automatic exposure time and analog gain (computed by the algorithm)\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::automatic.exposure\n+ * \\brief Automatic exposure time expressed as a number of lines\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::automatic.gain\n+ * \\brief Automatic analogue gain multiplier\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::automatic.quantizationGain\n+ * \\brief Automatic quantization gain multiplier\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::automatic.yTarget\n+ * \\brief Automatically determined luminance target\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::autoExposureEnabled\n+ * \\brief Manual/automatic AGC state (exposure) as set by the ExposureTimeMode control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::autoGainEnabled\n+ * \\brief Manual/automatic AGC state (gain) as set by the AnalogueGainMode control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::exposureValue\n+ * \\brief Exposure value as set by the ExposureValue control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::constraintMode\n+ * \\brief Constraint mode as set by the AeConstraintMode control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::exposureMode\n+ * \\brief Exposure mode as set by the AeExposureMode control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::minFrameDuration\n+ * \\brief Minimum frame duration as set by the FrameDurationLimits control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ActiveState::maxFrameDuration\n+ * \\brief Maximum frame duration as set by the FrameDurationLimits control\n+ */\n+\n+/**\n+ * \\struct AgcMeanLuminanceAlgorithm::FrameContext\n+ * \\brief Per-frame context for AgcMeanLuminanceAlgorithm\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::exposure\n+ * \\brief Exposure time expressed as a number of lines computed by the algorithm\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::gain\n+ * \\brief Analogue gain multiplier computed by the algorithm\n+ *\n+ * The gain should be adapted to the sensor specific gain code before applying.\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::quantizationGain\n+ * \\brief Quantization gain multiplier computed by the algorithm\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::exposureValue\n+ * \\brief Exposure value as set by the ExposureValue control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::yTarget\n+ * \\brief Luminance target computed by the algorithm\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::vblank\n+ * \\brief Vertical blanking parameter computed by the algorithm\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::autoExposureEnabled\n+ * \\brief Manual/automatic AGC state (exposure) as set by the ExposureTimeMode control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::autoGainEnabled\n+ * \\brief Manual/automatic AGC state (gain) as set by the AnalogueGainMode control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::constraintMode\n+ * \\brief Constraint mode as set by the AeConstraintMode control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::exposureMode\n+ * \\brief Exposure mode as set by the AeExposureMode control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::minFrameDuration\n+ * \\brief Minimum frame duration as set by the FrameDurationLimits control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::maxFrameDuration\n+ * \\brief Maximum frame duration as set by the FrameDurationLimits control\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::frameDuration\n+ * \\brief The actual FrameDuration used by the algorithm for the frame\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::autoExposureModeChange\n+ * \\brief Indicate if autoExposureEnabled has changed from true in the previous\n+ * frame to false in the current frame, and no manual exposure value has been\n+ * supplied in the current frame.\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::FrameContext::autoGainModeChange\n+ * \\brief Indicate if autoGainEnabled has changed from true in the previous\n+ * frame to false in the current frame, and no manual gain value has been\n+ * supplied in the current frame.\n+ */\n+\n+/**\n+ * \\struct AgcMeanLuminanceAlgorithm::ConfigurationParams\n+ * \\brief Parameters for AgcMeanLuminanceAlgorithm::configure()\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ConfigurationParams::sensor\n+ * \\brief CameraSensorHelper for the sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ConfigurationParams::sensorInfo\n+ * \\brief Details of the sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ConfigurationParams::sensorControls\n+ * \\brief ControlInfoMap of the sensor\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ConfigurationParams::ctrlMap\n+ * \\brief ControlMap to update with controls\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ConfigurationParams::autoAllowed\n+ * \\brief Whether to enable auto controls\n+ */\n+\n+/**\n+ * \\struct AgcMeanLuminanceAlgorithm::ProcessParams\n+ * \\brief Parameters for AgcMeanLuminanceAlgorithm::process()\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ProcessParams::traits\n+ * \\brief Implementation of AgcMeanLuminance::Traits\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ProcessParams::hist\n+ * \\brief Luminance histogram of the frame\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ProcessParams::exposure\n+ * \\brief Effective exposure of the frame\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ProcessParams::gain\n+ * \\brief Effective gain of the frame\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ProcessParams::additionalConstraints\n+ * \\brief Addition AgcMeanLuminance::AgcConstraints to apply\n+ *\n+ * \\var AgcMeanLuminanceAlgorithm::ProcessParams::lux\n+ * \\brief Lux value for the frame\n+ */\n+\n+/**\n+ * \\brief Load tuning data\n+ */\n+int AgcMeanLuminanceAlgorithm::init(const ValueNode &tuningData)\n+{\n+\tint ret = impl_.parseTuningData(tuningData);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Initialize the session configuration and active state\n+ */\n+int AgcMeanLuminanceAlgorithm::configure(Session &session, ActiveState &state, const ConfigurationParams &config)\n+{\n+\tsession = {};\n+\tsession.lineDuration = config.sensorInfo.minLineLength * 1.0s\n+\t\t/ config.sensorInfo.pixelRate;\n+\tsession.sensor.outputSize = config.sensorInfo.outputSize;\n+\tsession.autoAllowed = config.autoAllowed;\n+\n+\tconst double lineDurationUs = session.lineDuration.get<std::micro>();\n+\n+\t/*\n+\t * Compute exposure time limits from the V4L2_CID_EXPOSURE control\n+\t * limits and the line duration.\n+\t */\n+\n+\tconst ControlInfo &v4l2Exposure = config.sensorControls.find(V4L2_CID_EXPOSURE)->second;\n+\tint32_t minExposure = v4l2Exposure.min().get<int32_t>();\n+\tint32_t maxExposure = v4l2Exposure.max().get<int32_t>();\n+\tint32_t defExposure = v4l2Exposure.def().get<int32_t>();\n+\tconfig.ctrlMap[&controls::ExposureTime] = ControlInfo{\n+\t\tstatic_cast<int32_t>(minExposure * lineDurationUs),\n+\t\tstatic_cast<int32_t>(maxExposure * lineDurationUs),\n+\t\tstatic_cast<int32_t>(defExposure * lineDurationUs),\n+\t};\n+\n+\t/* Compute the analogue gain limits. */\n+\tconst ControlInfo &v4l2Gain = config.sensorControls.find(V4L2_CID_ANALOGUE_GAIN)->second;\n+\tfloat minGain = config.sensor.gain(v4l2Gain.min().get<int32_t>());\n+\tfloat maxGain = config.sensor.gain(v4l2Gain.max().get<int32_t>());\n+\tfloat defGain = config.sensor.gain(v4l2Gain.def().get<int32_t>());\n+\tconfig.ctrlMap[&controls::AnalogueGain] = ControlInfo{\n+\t\tminGain,\n+\t\tmaxGain,\n+\t\tdefGain,\n+\t};\n+\n+\tLOG(AgcMeanLuminance, Debug)\n+\t\t<< \"Exposure: [\" << minExposure << \", \" << maxExposure\n+\t\t<< \"], gain: [\" << minGain << \", \" << maxGain << \"]\";\n+\n+\t/*\n+\t* Compute the frame duration limits.\n+\t*\n+\t* The frame length is computed assuming a fixed line length combined\n+\t* with the vertical frame sizes.\n+\t*/\n+\tconst ControlInfo &v4l2HBlank = config.sensorControls.find(V4L2_CID_HBLANK)->second;\n+\tuint32_t hblank = v4l2HBlank.def().get<int32_t>();\n+\tuint32_t lineLength = config.sensorInfo.outputSize.width + hblank;\n+\n+\tconst ControlInfo &v4l2VBlank = config.sensorControls.find(V4L2_CID_VBLANK)->second;\n+\tstd::array<uint32_t, 3> frameHeights{\n+\t\tv4l2VBlank.min().get<int32_t>() + config.sensorInfo.outputSize.height,\n+\t\tv4l2VBlank.max().get<int32_t>() + config.sensorInfo.outputSize.height,\n+\t\tv4l2VBlank.def().get<int32_t>() + config.sensorInfo.outputSize.height,\n+\t};\n+\n+\tstd::array<int64_t, 3> frameDurations;\n+\tfor (unsigned int i = 0; i < frameHeights.size(); ++i) {\n+\t\tuint64_t frameSize = lineLength * frameHeights[i];\n+\t\tframeDurations[i] = frameSize / (config.sensorInfo.pixelRate / 1000000U);\n+\t}\n+\n+\tconfig.ctrlMap[&controls::FrameDurationLimits] = ControlInfo{\n+\t\tframeDurations[0],\n+\t\tframeDurations[1],\n+\t\tSpan<const int64_t, 2>{ { frameDurations[2], frameDurations[2] } },\n+\t};\n+\n+\tsession.minFrameDuration = std::chrono::microseconds(frameDurations[0]);\n+\tsession.maxFrameDuration = std::chrono::microseconds(frameDurations[1]);\n+\n+\t/*\n+\t * When the AGC computes the new exposure values for a frame, it needs\n+\t * to know the limits for exposure time and analogue gain. As it depends\n+\t * on the sensor, update it with the controls.\n+\t *\n+\t * \\todo take VBLANK into account for maximum exposure time\n+\t */\n+\tsession.minExposureTime = minExposure * session.lineDuration;\n+\tsession.maxExposureTime = maxExposure * session.lineDuration;\n+\tsession.minAnalogueGain = minGain;\n+\tsession.maxAnalogueGain = maxGain;\n+\n+\timpl_.configure(session.lineDuration, &config.sensor);\n+\timpl_.setLimits(session.minExposureTime, session.maxExposureTime,\n+\t\t\tsession.minAnalogueGain, session.maxAnalogueGain,\n+\t\t\t{});\n+\timpl_.resetFrameCount();\n+\n+\t/* Configure the default exposure and gain. */\n+\tstate = {};\n+\tstate.automatic.gain = session.minAnalogueGain;\n+\tstate.automatic.exposure = 10ms / session.lineDuration;\n+\tstate.automatic.quantizationGain = 1;\n+\tstate.automatic.yTarget = impl_.effectiveYTarget();\n+\tstate.manual.gain = state.automatic.gain;\n+\tstate.manual.exposure = state.automatic.exposure;\n+\tstate.autoExposureEnabled = session.autoAllowed;\n+\tstate.autoGainEnabled = session.autoAllowed;\n+\tstate.exposureValue = 0;\n+\n+\tstate.constraintMode =\n+\t\tstatic_cast<controls::AeConstraintModeEnum>(impl_.constraintModes().begin()->first);\n+\tstate.exposureMode =\n+\t\tstatic_cast<controls::AeExposureModeEnum>(impl_.exposureModeHelpers().begin()->first);\n+\n+\tstate.minFrameDuration = session.minFrameDuration;\n+\tstate.maxFrameDuration = session.maxFrameDuration;\n+\n+\tconst auto add = [&](const ControlId &cid, const auto &automatic, const auto &manual) {\n+\t\tstd::array<ControlValue, 2> values;\n+\t\tsize_t count = 0;\n+\n+\t\tif (session.autoAllowed)\n+\t\t\tvalues[count++] = ControlValue(automatic);\n+\n+\t\tvalues[count++] = ControlValue(manual);\n+\n+\t\tconfig.ctrlMap[&cid] = ControlInfo{\n+\t\t\t{ values.data(), count },\n+\t\t\tControlValue(session.autoAllowed ? automatic : manual),\n+\t\t};\n+\t};\n+\n+\tadd(controls::ExposureTimeMode, controls::ExposureTimeModeAuto, controls::ExposureTimeModeManual);\n+\tadd(controls::AnalogueGainMode, controls::AnalogueGainModeAuto, controls::AnalogueGainModeManual);\n+\n+\t/* \\todo Move this to the `Camera` class. */\n+\tconfig.ctrlMap[&controls::AeEnable] = ControlInfo{\n+\t\tfalse,\n+\t\tsession.autoAllowed,\n+\t\tsession.autoAllowed,\n+\t};\n+\n+\t// \\todo Should these be added/removed based on `session.autoAllowed` ?\n+\tconfig.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f);\n+\n+\tfor (const auto &[id, info] : impl_.controls())\n+\t\tconfig.ctrlMap[id] = info;\n+\n+\treturn 0;\n+}\n+\n+/**\n+ * \\brief Handle a \\a queueRequest operation\n+ */\n+void AgcMeanLuminanceAlgorithm::queueRequest(const Session &session, ActiveState &state,\n+\t\t\t\tFrameContext &frameContext, const ControlList &controls)\n+{\n+\tif (session.autoAllowed) {\n+\t\tconst auto &aeEnable = controls.get(controls::ExposureTimeMode);\n+\t\tif (aeEnable &&\n+\t\t (*aeEnable == controls::ExposureTimeModeAuto) != state.autoExposureEnabled) {\n+\t\t\tstate.autoExposureEnabled = (*aeEnable == controls::ExposureTimeModeAuto);\n+\n+\t\t\tLOG(AgcMeanLuminance, Debug)\n+\t\t\t\t<< (state.autoExposureEnabled ? \"Enabling\" : \"Disabling\")\n+\t\t\t\t<< \" AGC (exposure)\";\n+\n+\t\t\t/*\n+\t\t\t * If we go from auto -> manual with no manual control\n+\t\t\t * set, use the last computed value, which we don't\n+\t\t\t * know until prepare() so save this information.\n+\t\t\t *\n+\t\t\t * \\todo Check the previous frame at prepare() time\n+\t\t\t * instead of saving a flag here\n+\t\t\t */\n+\t\t\tif (!state.autoExposureEnabled && !controls.get(controls::ExposureTime))\n+\t\t\t\tframeContext.autoExposureModeChange = true;\n+\t\t}\n+\n+\t\tconst auto &agEnable = controls.get(controls::AnalogueGainMode);\n+\t\tif (agEnable &&\n+\t\t (*agEnable == controls::AnalogueGainModeAuto) != state.autoGainEnabled) {\n+\t\t\tstate.autoGainEnabled = (*agEnable == controls::AnalogueGainModeAuto);\n+\n+\t\t\tLOG(AgcMeanLuminance, Debug)\n+\t\t\t\t<< (state.autoGainEnabled ? \"Enabling\" : \"Disabling\")\n+\t\t\t\t<< \" AGC (gain)\";\n+\t\t\t/*\n+\t\t\t * If we go from auto -> manual with no manual control\n+\t\t\t * set, use the last computed value, which we don't\n+\t\t\t * know until prepare() so save this information.\n+\t\t\t */\n+\t\t\tif (!state.autoGainEnabled && !controls.get(controls::AnalogueGain))\n+\t\t\t\tframeContext.autoGainModeChange = true;\n+\t\t}\n+\t}\n+\n+\tconst auto &exposure = controls.get(controls::ExposureTime);\n+\tif (exposure && !state.autoExposureEnabled) {\n+\t\tstate.manual.exposure = *exposure * 1.0us / session.lineDuration;\n+\n+\t\tLOG(AgcMeanLuminance, Debug)\n+\t\t\t<< \"Set exposure to \" << state.manual.exposure;\n+\t}\n+\n+\tconst auto &gain = controls.get(controls::AnalogueGain);\n+\tif (gain && !state.autoGainEnabled) {\n+\t\tstate.manual.gain = *gain;\n+\n+\t\tLOG(AgcMeanLuminance, Debug) << \"Set gain to \" << state.manual.gain;\n+\t}\n+\n+\tframeContext.autoExposureEnabled = state.autoExposureEnabled;\n+\tframeContext.autoGainEnabled = state.autoGainEnabled;\n+\n+\tif (!frameContext.autoExposureEnabled)\n+\t\tframeContext.exposure = state.manual.exposure;\n+\tif (!frameContext.autoGainEnabled)\n+\t\tframeContext.gain = state.manual.gain;\n+\n+\tif (!frameContext.autoExposureEnabled &&\n+\t !frameContext.autoGainEnabled)\n+\t\tframeContext.quantizationGain = 1.0;\n+\n+\tconst auto &exposureMode = controls.get(controls::AeExposureMode);\n+\tif (exposureMode)\n+\t\tstate.exposureMode =\n+\t\t\tstatic_cast<controls::AeExposureModeEnum>(*exposureMode);\n+\tframeContext.exposureMode = state.exposureMode;\n+\n+\tconst auto &constraintMode = controls.get(controls::AeConstraintMode);\n+\tif (constraintMode)\n+\t\tstate.constraintMode =\n+\t\t\tstatic_cast<controls::AeConstraintModeEnum>(*constraintMode);\n+\tframeContext.constraintMode = state.constraintMode;\n+\n+\tconst auto &exposureValue = controls.get(controls::ExposureValue);\n+\tif (exposureValue)\n+\t\tstate.exposureValue = *exposureValue;\n+\tframeContext.exposureValue = state.exposureValue;\n+\n+\tconst auto &frameDurationLimits = controls.get(controls::FrameDurationLimits);\n+\tif (frameDurationLimits) {\n+\t\t/* Limit the control value to the limits in ControlInfo */\n+\t\tstate.minFrameDuration = std::clamp<utils::Duration>(\n+\t\t\tstd::chrono::microseconds((*frameDurationLimits).front()),\n+\t\t\tsession.minFrameDuration,\n+\t\t\tsession.maxFrameDuration\n+\t\t);\n+\n+\t\tstate.maxFrameDuration = std::clamp<utils::Duration>(\n+\t\t\tstd::chrono::microseconds((*frameDurationLimits).back()),\n+\t\t\tsession.minFrameDuration,\n+\t\t\tsession.maxFrameDuration\n+\t\t);\n+\t}\n+\tframeContext.minFrameDuration = state.minFrameDuration;\n+\tframeContext.maxFrameDuration = state.maxFrameDuration;\n+}\n+\n+/**\n+ * \\brief Handle a \\a prepare operation\n+ */\n+void AgcMeanLuminanceAlgorithm::prepare(ActiveState &state, FrameContext &frameContext)\n+{\n+\tuint32_t activeAutoExposure = state.automatic.exposure;\n+\tdouble activeAutoGain = state.automatic.gain;\n+\tdouble activeAutoQGain = state.automatic.quantizationGain;\n+\n+\t/* Populate exposure and gain in auto mode */\n+\tif (frameContext.autoExposureEnabled) {\n+\t\tframeContext.exposure = activeAutoExposure;\n+\t\tframeContext.quantizationGain = activeAutoQGain;\n+\t}\n+\tif (frameContext.autoGainEnabled) {\n+\t\tframeContext.gain = activeAutoGain;\n+\t\tframeContext.quantizationGain = activeAutoQGain;\n+\t}\n+\n+\t/*\n+\t * Populate manual exposure and gain from the active auto values when\n+\t * transitioning from auto to manual\n+\t */\n+\tif (!frameContext.autoExposureEnabled && frameContext.autoExposureModeChange) {\n+\t\tstate.manual.exposure = activeAutoExposure;\n+\t\tframeContext.exposure = activeAutoExposure;\n+\t}\n+\tif (!frameContext.autoGainEnabled && frameContext.autoGainModeChange) {\n+\t\tstate.manual.gain = activeAutoGain;\n+\t\tframeContext.gain = activeAutoGain;\n+\t\tframeContext.quantizationGain = activeAutoQGain;\n+\t}\n+\n+\tframeContext.yTarget = state.automatic.yTarget;\n+}\n+\n+/**\n+ * \\brief Handle a \\a process operation\n+ */\n+void AgcMeanLuminanceAlgorithm::process(const Session &session, ActiveState &state,\n+\t\t\t\t\tFrameContext &frameContext, std::optional<ProcessParams> &¶ms,\n+\t\t\t\t\tControlList &metadata)\n+{\n+\tconst utils::Duration &lineDuration = session.lineDuration;\n+\tutils::Duration newExposureTime = {};\n+\n+\tif (params) {\n+\t\tASSERT(session.autoAllowed);\n+\n+\t\t/*\n+\t\t* Set the AGC limits using the fixed exposure time and/or gain in\n+\t\t* manual mode, or the sensor limits in auto mode.\n+\t\t*/\n+\t\tutils::Duration minExposureTime;\n+\t\tutils::Duration maxExposureTime;\n+\t\tdouble minAnalogueGain;\n+\t\tdouble maxAnalogueGain;\n+\n+\t\tif (frameContext.autoExposureEnabled) {\n+\t\t\tminExposureTime = session.minExposureTime;\n+\t\t\tmaxExposureTime = std::clamp(frameContext.maxFrameDuration, session.minExposureTime, session.maxExposureTime);\n+\t\t} else {\n+\t\t\tminExposureTime = lineDuration * frameContext.exposure;\n+\t\t\tmaxExposureTime = minExposureTime;\n+\t\t}\n+\n+\t\tif (frameContext.autoGainEnabled) {\n+\t\t\tminAnalogueGain = session.minAnalogueGain;\n+\t\t\tmaxAnalogueGain = session.maxAnalogueGain;\n+\t\t} else {\n+\t\t\tminAnalogueGain = frameContext.gain;\n+\t\t\tmaxAnalogueGain = frameContext.gain;\n+\t\t}\n+\n+\t\t/*\n+\t\t* The Agc algorithm needs to know the effective exposure value that was\n+\t\t* applied to the sensor when the statistics were collected.\n+\t\t*/\n+\t\tutils::Duration effectiveExposureValue =\n+\t\t\tlineDuration * params->exposure * params->gain;\n+\n+\t\timpl_.setLimits(minExposureTime, maxExposureTime,\n+\t\t\t\tminAnalogueGain, maxAnalogueGain,\n+\t\t\t\tstd::move(params->additionalConstraints));\n+\n+\t\timpl_.setExposureCompensation(pow(2.0, frameContext.exposureValue));\n+\t\timpl_.setLux(params->lux);\n+\n+\t\tdouble aGain, qGain, dGain;\n+\t\tstd::tie(newExposureTime, aGain, qGain, dGain) =\n+\t\t\timpl_.calculateNewEv(frameContext.constraintMode, frameContext.exposureMode,\n+\t\t\t\t\t params->hist, effectiveExposureValue, params->traits);\n+\n+\t\tLOG(AgcMeanLuminance, Debug)\n+\t\t\t<< \"Divided up exposure time, analogue gain, quantization gain\"\n+\t\t\t<< \" and digital gain are \" << newExposureTime << \", \" << aGain\n+\t\t\t<< \", \" << qGain << \" and \" << dGain;\n+\n+\t\t/* Update the estimated exposure and gain. */\n+\t\tstate.automatic.exposure = newExposureTime / lineDuration;\n+\t\tstate.automatic.gain = aGain;\n+\t\tstate.automatic.quantizationGain = qGain;\n+\t\tstate.automatic.yTarget = impl_.effectiveYTarget();\n+\t}\n+\n+\t/*\n+\t * Expand the target frame duration so that we do not run faster than\n+\t * the minimum frame duration when we have short exposures.\n+\t */\n+\tconst auto frameDuration = std::max(frameContext.minFrameDuration, newExposureTime);\n+\tframeContext.vblank = (frameDuration / lineDuration) - session.sensor.outputSize.height;\n+\n+\t/* Update frame duration accounting for line length quantization. */\n+\tframeContext.frameDuration = (session.sensor.outputSize.height + frameContext.vblank) * lineDuration;\n+\n+\tmetadata.set(controls::AnalogueGain, frameContext.gain);\n+\tmetadata.set(controls::ExposureTime, utils::Duration(lineDuration * frameContext.exposure).get<std::micro>());\n+\tmetadata.set(controls::FrameDuration, frameContext.frameDuration.get<std::micro>());\n+\tmetadata.set(controls::ExposureTimeMode,\n+\t\t frameContext.autoExposureEnabled\n+\t\t ? controls::ExposureTimeModeAuto\n+\t\t : controls::ExposureTimeModeManual);\n+\tmetadata.set(controls::AnalogueGainMode,\n+\t\t frameContext.autoGainEnabled\n+\t\t ? controls::AnalogueGainModeAuto\n+\t\t : controls::AnalogueGainModeManual);\n+\n+\tmetadata.set(controls::AeExposureMode, frameContext.exposureMode);\n+\tmetadata.set(controls::AeConstraintMode, frameContext.constraintMode);\n+\tmetadata.set(controls::ExposureValue, frameContext.exposureValue);\n+}\n+\n } /* namespace ipa */\n \n } /* namespace libcamera */\ndiff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h\nindex f4e1680ab5..3378ec7faa 100644\n--- a/src/ipa/libipa/agc_mean_luminance.h\n+++ b/src/ipa/libipa/agc_mean_luminance.h\n@@ -9,13 +9,17 @@\n \n #include <map>\n #include <memory>\n+#include <optional>\n #include <tuple>\n #include <vector>\n \n #include <libcamera/base/utils.h>\n \n+#include <libcamera/control_ids.h>\n #include <libcamera/controls.h>\n \n+#include <libcamera/ipa/core_ipa_interface.h>\n+\n #include \"libcamera/internal/value_node.h\"\n \n #include \"exposure_mode_helper.h\"\n@@ -115,6 +119,98 @@ private:\n \tControlInfoMap::Map controls_;\n };\n \n+class AgcMeanLuminanceAlgorithm\n+{\n+public:\n+\tstruct Session {\n+\t\tutils::Duration minExposureTime;\n+\t\tutils::Duration maxExposureTime;\n+\t\tdouble minAnalogueGain;\n+\t\tdouble maxAnalogueGain;\n+\t\tutils::Duration minFrameDuration;\n+\t\tutils::Duration maxFrameDuration;\n+\n+\t\tutils::Duration lineDuration;\n+\n+\t\tstruct {\n+\t\t\tSize outputSize;\n+\t\t} sensor;\n+\n+\t\tbool autoAllowed;\n+\t};\n+\n+\tstruct ActiveState {\n+\t\tstruct {\n+\t\t\tuint32_t exposure;\n+\t\t\tdouble gain;\n+\t\t} manual;\n+\t\tstruct {\n+\t\t\tuint32_t exposure;\n+\t\t\tdouble gain;\n+\t\t\tdouble quantizationGain;\n+\t\t\tdouble yTarget;\n+\t\t} automatic;\n+\n+\t\tbool autoExposureEnabled;\n+\t\tbool autoGainEnabled;\n+\t\tdouble exposureValue;\n+\t\tcontrols::AeConstraintModeEnum constraintMode;\n+\t\tcontrols::AeExposureModeEnum exposureMode;\n+\t\tutils::Duration minFrameDuration;\n+\t\tutils::Duration maxFrameDuration;\n+\t};\n+\n+\tstruct FrameContext {\n+\t\tuint32_t exposure;\n+\t\tdouble gain;\n+\t\tdouble quantizationGain;\n+\t\tdouble exposureValue;\n+\t\tdouble yTarget;\n+\t\tuint32_t vblank;\n+\t\tbool autoExposureEnabled;\n+\t\tbool autoGainEnabled;\n+\t\tcontrols::AeConstraintModeEnum constraintMode;\n+\t\tcontrols::AeExposureModeEnum exposureMode;\n+\t\tutils::Duration minFrameDuration;\n+\t\tutils::Duration maxFrameDuration;\n+\t\tutils::Duration frameDuration;\n+\t\tbool autoExposureModeChange;\n+\t\tbool autoGainModeChange;\n+\t};\n+\n+\tstruct ConfigurationParams {\n+\t\tconst CameraSensorHelper &sensor;\n+\t\tconst IPACameraSensorInfo &sensorInfo;\n+\t\tconst ControlInfoMap &sensorControls;\n+\t\tControlInfoMap::Map &ctrlMap;\n+\t\tbool autoAllowed = true;\n+\t};\n+\n+\tint init(const ValueNode &tuningData);\n+\n+\tint configure(Session &session, ActiveState &state, const ConfigurationParams &config);\n+\n+\tvoid queueRequest(const Session &session, ActiveState &state,\n+\t\t\t FrameContext &frameContext, const ControlList &controls);\n+\n+\tvoid prepare(ActiveState &state, FrameContext &frameContext);\n+\n+\tstruct ProcessParams {\n+\t\tconst AgcMeanLuminance::Traits &traits;\n+\t\tconst Histogram &hist;\n+\t\tuint32_t exposure;\n+\t\tdouble gain;\n+\t\tstd::vector<AgcMeanLuminance::AgcConstraint> &&additionalConstraints = {};\n+\t\tunsigned int lux = 0;\n+\t};\n+\n+\tvoid process(const Session &session, ActiveState &state, FrameContext &frameContext,\n+\t\t std::optional<ProcessParams> &¶ms, ControlList &metadata);\n+\n+private:\n+\tAgcMeanLuminance impl_;\n+};\n+\n } /* namespace ipa */\n \n } /* namespace libcamera */\n", "prefixes": [ "RFC", "v1", "10/17" ] }