Patch Detail
Show a patch.
GET /api/patches/27183/?format=api
{ "id": 27183, "url": "https://patchwork.libcamera.org/api/patches/27183/?format=api", "web_url": "https://patchwork.libcamera.org/patch/27183/", "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-5-barnabas.pocze@ideasonboard.com>", "date": "2026-07-03T15:38:06", "name": "[RFC,v1,04/17] ipa: libipa: agc_mean_luminance: Remove the need for inheritance", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "117a95f4b137af3622dd03b3d52d4d1d45cd7fa5", "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/27183/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/27183/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/27183/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 0B240C3302\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 3 Jul 2026 15:38:37 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 43A2665FCE;\n\tFri, 3 Jul 2026 17:38:30 +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 AF29665FC9\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 3 Jul 2026 17:38:23 +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 81BA31494\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 3 Jul 2026 17:37:37 +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=\"chkZsJY3\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1783093057;\n\tbh=9quqwvpZ6B8TLCeNodTbKpKCGXDei4Hb7D+OLbi0SGA=;\n\th=From:To:Subject:Date:In-Reply-To:References:From;\n\tb=chkZsJY3FCiVLx0UMOxFMDuBu7QA0e9r41mgQijXpZRTgLd8E22VcDv9fUvjA523E\n\tvV+1QVcfrjQFtPleKfYcjHt66tNN+DmeACMAEUJ7MnJLck3Ev7ozsbMsy4HOaOD/y8\n\tNoHsXWuUr748d2ReScH0+Cq9IUiYDaFsgCr/efH4=", "From": "=?utf-8?q?Barnab=C3=A1s_P=C5=91cze?= <barnabas.pocze@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Subject": "[RFC PATCH v1 04/17] ipa: libipa: agc_mean_luminance: Remove the\n\tneed for inheritance", "Date": "Fri, 3 Jul 2026 17:38:06 +0200", "Message-ID": "<20260703153819.1088752-5-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": "Create a separate type with the necessary callbacks instead of using\ninheritance. This removes the need for users to have to use member\nvariables to store temporary data that is only needed for luminance\nestimation.\n\nSigned-off-by: Barnabás Pőcze <barnabas.pocze@ideasonboard.com>\n---\n src/ipa/ipu3/algorithms/agc.cpp | 111 ++++++++++++-------\n src/ipa/ipu3/algorithms/agc.h | 8 +-\n src/ipa/libipa/agc_mean_luminance.cpp | 52 +++++----\n src/ipa/libipa/agc_mean_luminance.h | 14 ++-\n src/ipa/mali-c55/algorithms/agc.cpp | 57 ++++++----\n src/ipa/mali-c55/algorithms/agc.h | 4 +-\n src/ipa/rkisp1/algorithms/agc.cpp | 154 ++++++++++++++------------\n src/ipa/rkisp1/algorithms/agc.h | 7 +-\n 8 files changed, 238 insertions(+), 169 deletions(-)", "diff": "diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp\nindex d6a7036c65..5b152dcda2 100644\n--- a/src/ipa/ipu3/algorithms/agc.cpp\n+++ b/src/ipa/ipu3/algorithms/agc.cpp\n@@ -76,11 +76,11 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData)\n {\n \tint ret;\n \n-\tret = parseTuningData(tuningData);\n+\tret = agc_.parseTuningData(tuningData);\n \tif (ret)\n \t\treturn ret;\n \n-\tcontext.ctrlMap.merge(controls());\n+\tcontext.ctrlMap.merge(agc_.controls());\n \n \treturn 0;\n }\n@@ -112,13 +112,13 @@ int Agc::configure(IPAContext &context,\n \tactiveState.agc.gain = minAnalogueGain_;\n \tactiveState.agc.exposure = 10ms / configuration.sensor.lineDuration;\n \n-\tcontext.activeState.agc.constraintMode = constraintModes().begin()->first;\n-\tcontext.activeState.agc.exposureMode = exposureModeHelpers().begin()->first;\n+\tcontext.activeState.agc.constraintMode = agc_.constraintModes().begin()->first;\n+\tcontext.activeState.agc.exposureMode = agc_.exposureModeHelpers().begin()->first;\n \n \t/* \\todo Run this again when FrameDurationLimits is passed in */\n-\tsetLimits(minExposureTime_, maxExposureTime_, minAnalogueGain_,\n-\t\t maxAnalogueGain_, {});\n-\tresetFrameCount();\n+\tagc_.setLimits(minExposureTime_, maxExposureTime_, minAnalogueGain_,\n+\t\t maxAnalogueGain_, {});\n+\tagc_.resetFrameCount();\n \n \treturn 0;\n }\n@@ -156,39 +156,58 @@ Histogram Agc::parseStatistics(const ipu3_uapi_stats_3a *stats,\n \treturn Histogram(Span<uint32_t>(hist));\n }\n \n-/**\n- * \\brief Estimate the relative luminance of the frame with a given gain\n- * \\param[in] gain The gain to apply in estimating luminance\n- *\n- * The estimation is based on the AWB statistics for the current frame. Red,\n- * green and blue averages for all cells are first multiplied by the gain, and\n- * then saturated to approximate the sensor behaviour at high brightness\n- * values. The approximation is quite rough, as it doesn't take into account\n- * non-linearities when approaching saturation.\n- *\n- * The relative luminance (Y) is computed from the linear RGB components using\n- * the Rec. 601 formula. The values are normalized to the [0.0, 1.0] range,\n- * where 1.0 corresponds to a theoretical perfect reflector of 100% reference\n- * white.\n- *\n- * More detailed information can be found in:\n- * https://en.wikipedia.org/wiki/Relative_luminance\n- *\n- * \\return The relative luminance of the frame\n- */\n-double Agc::estimateLuminance(double gain) const\n+namespace {\n+\n+class AgcTraits final : public AgcMeanLuminance::Traits\n {\n-\tRGB<double> sum{ 0.0 };\n+public:\n+\tAgcTraits(Span<const std::tuple<uint8_t, uint8_t, uint8_t>> rgbTriples,\n+\t\t RGB<double> gains, const ipu3_uapi_grid_config &bdsGrid)\n+\t\t: rgbTriples_(rgbTriples), gains_(gains), bdsGrid_(bdsGrid)\n+\t{\n+\t}\n \n-\tfor (unsigned int i = 0; i < rgbTriples_.size(); i++) {\n-\t\tsum.r() += std::min(std::get<0>(rgbTriples_[i]) * gain, 255.0);\n-\t\tsum.g() += std::min(std::get<1>(rgbTriples_[i]) * gain, 255.0);\n-\t\tsum.b() += std::min(std::get<2>(rgbTriples_[i]) * gain, 255.0);\n+\t/**\n+\t * \\brief Estimate the relative luminance of the frame with a given gain\n+\t * \\param[in] gain The gain to apply in estimating luminance\n+\t *\n+\t * The estimation is based on the AWB statistics for the current frame. Red,\n+\t * green and blue averages for all cells are first multiplied by the gain, and\n+\t * then saturated to approximate the sensor behaviour at high brightness\n+\t * values. The approximation is quite rough, as it doesn't take into account\n+\t * non-linearities when approaching saturation.\n+\t *\n+\t * The relative luminance (Y) is computed from the linear RGB components using\n+\t * the Rec. 601 formula. The values are normalized to the [0.0, 1.0] range,\n+\t * where 1.0 corresponds to a theoretical perfect reflector of 100% reference\n+\t * white.\n+\t *\n+\t * More detailed information can be found in:\n+\t * https://en.wikipedia.org/wiki/Relative_luminance\n+\t *\n+\t * \\return The relative luminance of the frame\n+\t */\n+\n+\tdouble estimateLuminance(double gain) const override\n+\t{\n+\t\tRGB<double> sum{ 0.0 };\n+\n+\t\tfor (unsigned int i = 0; i < rgbTriples_.size(); i++) {\n+\t\t\tsum.r() += std::min(std::get<0>(rgbTriples_[i]) * gain, 255.0);\n+\t\t\tsum.g() += std::min(std::get<1>(rgbTriples_[i]) * gain, 255.0);\n+\t\t\tsum.b() += std::min(std::get<2>(rgbTriples_[i]) * gain, 255.0);\n+\t\t}\n+\n+\t\tdouble ySum = rec601LuminanceFromRGB(sum * gains_);\n+\t\treturn ySum / (bdsGrid_.height * bdsGrid_.width) / 255;\n \t}\n \n-\tRGB<double> gains{{ rGain_, gGain_, bGain_ }};\n-\tdouble ySum = rec601LuminanceFromRGB(sum * gains);\n-\treturn ySum / (bdsGrid_.height * bdsGrid_.width) / 255;\n+private:\n+\tSpan<const std::tuple<uint8_t, uint8_t, uint8_t>> rgbTriples_;\n+\tRGB<double> gains_;\n+\tconst ipu3_uapi_grid_config &bdsGrid_;\n+};\n+\n }\n \n /**\n@@ -208,9 +227,7 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \t\t ControlList &metadata)\n {\n \tHistogram hist = parseStatistics(stats, context.configuration.grid.bdsGrid);\n-\trGain_ = context.activeState.awb.gains.red;\n-\tgGain_ = context.activeState.awb.gains.blue;\n-\tbGain_ = context.activeState.awb.gains.green;\n+\n \n \t/*\n \t * The Agc algorithm needs to know the effective exposure value that was\n@@ -221,12 +238,22 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \tdouble analogueGain = frameContext.sensor.gain;\n \tutils::Duration effectiveExposureValue = exposureTime * analogueGain;\n \n+\tAgcTraits agcTraits{\n+\t\trgbTriples_,\n+\t\t{{\n+\t\t\tcontext.activeState.awb.gains.red,\n+\t\t\tcontext.activeState.awb.gains.blue,\n+\t\t\tcontext.activeState.awb.gains.green,\n+\t\t}},\n+\t\tbdsGrid_,\n+\t};\n+\n \tutils::Duration newExposureTime;\n \tdouble aGain, qGain, dGain;\n \tstd::tie(newExposureTime, aGain, qGain, dGain) =\n-\t\tcalculateNewEv(context.activeState.agc.constraintMode,\n-\t\t\t context.activeState.agc.exposureMode, hist,\n-\t\t\t effectiveExposureValue);\n+\t\tagc_.calculateNewEv(context.activeState.agc.constraintMode,\n+\t\t\t\t context.activeState.agc.exposureMode, hist,\n+\t\t\t\t effectiveExposureValue, agcTraits);\n \n \tLOG(IPU3Agc, Debug)\n \t\t<< \"Divided up exposure time, analogue gain and digital gain are \"\ndiff --git a/src/ipa/ipu3/algorithms/agc.h b/src/ipa/ipu3/algorithms/agc.h\nindex d274a23504..d08da7600e 100644\n--- a/src/ipa/ipu3/algorithms/agc.h\n+++ b/src/ipa/ipu3/algorithms/agc.h\n@@ -24,7 +24,7 @@ struct IPACameraSensorInfo;\n \n namespace ipa::ipu3::algorithms {\n \n-class Agc : public Algorithm, public AgcMeanLuminance\n+class Agc : public Algorithm\n {\n public:\n \tAgc();\n@@ -38,7 +38,6 @@ public:\n \t\t ControlList &metadata) override;\n \n private:\n-\tdouble estimateLuminance(double gain) const override;\n \tHistogram parseStatistics(const ipu3_uapi_stats_3a *stats,\n \t\t\t\t const ipu3_uapi_grid_config &grid);\n \n@@ -49,11 +48,10 @@ private:\n \tdouble maxAnalogueGain_;\n \n \tuint32_t stride_;\n-\tdouble rGain_;\n-\tdouble gGain_;\n-\tdouble bGain_;\n \tipu3_uapi_grid_config bdsGrid_;\n \tstd::vector<std::tuple<uint8_t, uint8_t, uint8_t>> rgbTriples_;\n+\n+\tAgcMeanLuminance agc_;\n };\n \n } /* namespace ipa::ipu3::algorithms */\ndiff --git a/src/ipa/libipa/agc_mean_luminance.cpp b/src/ipa/libipa/agc_mean_luminance.cpp\nindex 2d3bc709f3..1529d55864 100644\n--- a/src/ipa/libipa/agc_mean_luminance.cpp\n+++ b/src/ipa/libipa/agc_mean_luminance.cpp\n@@ -21,7 +21,7 @@ using namespace libcamera::controls;\n \n /**\n * \\file agc_mean_luminance.h\n- * \\brief Base class implementing mean luminance AEGC\n+ * \\brief Class implementing mean luminance AEGC\n */\n \n namespace libcamera {\n@@ -105,6 +105,30 @@ static constexpr unsigned int kDefaultLuxLevel = 500;\n * \\brief The luminance target for the constraint\n */\n \n+/**\n+ * \\class AgcMeanLuminance::Traits\n+ * \\brief A collection of callbacks\n+ *\n+ * This type contains virtual methods that provide the necessary pieces of\n+ * information for the algorithm, and are to be implemented by the user.\n+ */\n+\n+ /**\n+ * \\fn AgcMeanLuminance::Traits::estimateLuminance(double gain)\n+ * \\brief Estimate the luminance of an image, adjusted by a given gain\n+ * \\param[in] gain The gain with which to adjust the luminance estimate\n+ *\n+ * This function estimates the average relative luminance of the frame that\n+ * would be output by the sensor if an additional \\a gain was applied. It is a\n+ * pure virtual function because estimation of luminance is a hardware-specific\n+ * operation, which depends wholly on the format of the stats that are delivered\n+ * to libcamera from the ISP. Derived classes must override this function with\n+ * one that calculates the normalised mean luminance value across the entire\n+ * image.\n+ *\n+ * \\return The normalised relative luminance of the image\n+ */\n+\n /**\n * \\class AgcMeanLuminance\n * \\brief A mean-based auto-exposure algorithm\n@@ -487,28 +511,12 @@ void AgcMeanLuminance::setLimits(utils::Duration minExposureTime,\n * \\brief Get the controls that have been generated after parsing tuning data\n */\n \n-/**\n- * \\fn AgcMeanLuminance::estimateLuminance(const double gain)\n- * \\brief Estimate the luminance of an image, adjusted by a given gain\n- * \\param[in] gain The gain with which to adjust the luminance estimate\n- *\n- * This function estimates the average relative luminance of the frame that\n- * would be output by the sensor if an additional \\a gain was applied. It is a\n- * pure virtual function because estimation of luminance is a hardware-specific\n- * operation, which depends wholly on the format of the stats that are delivered\n- * to libcamera from the ISP. Derived classes must override this function with\n- * one that calculates the normalised mean luminance value across the entire\n- * image.\n- *\n- * \\return The normalised relative luminance of the image\n- */\n-\n /**\n * \\brief Estimate the initial gain needed to achieve a relative luminance\n * target\n * \\return The calculated initial gain\n */\n-double AgcMeanLuminance::estimateInitialGain() const\n+double AgcMeanLuminance::estimateInitialGain(const Traits &traits) const\n {\n \tdouble yTarget = effectiveYTarget();\n \tdouble yGain = 1.0;\n@@ -520,7 +528,7 @@ double AgcMeanLuminance::estimateInitialGain() const\n \t* regions are saturated.\n \t*/\n \tfor (unsigned int i = 0; i < 8; i++) {\n-\t\tdouble yValue = estimateLuminance(yGain);\n+\t\tdouble yValue = traits.estimateLuminance(yGain);\n \t\tdouble extra_gain = std::min(10.0, yTarget / (yValue + .001));\n \n \t\tyGain *= extra_gain;\n@@ -663,6 +671,7 @@ utils::Duration AgcMeanLuminance::filterExposure(utils::Duration exposureValue)\n * the calculated gain\n * \\param[in] effectiveExposureValue The EV applied to the frame from which the\n * statistics in use derive\n+ * \\param[in] traits The traits object implementing the necessary functions\n *\n * Calculate a new exposure value to try to obtain the target. The calculated\n * exposure value is filtered to prevent rapid changes from frame to frame, and\n@@ -675,7 +684,8 @@ std::tuple<utils::Duration, double, double, double>\n AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex,\n \t\t\t\t uint32_t exposureModeIndex,\n \t\t\t\t const Histogram &yHist,\n-\t\t\t\t utils::Duration effectiveExposureValue)\n+\t\t\t\t utils::Duration effectiveExposureValue,\n+\t\t\t\t const Traits &traits)\n {\n \t/*\n \t * The pipeline handler should validate that we have received an allowed\n@@ -696,7 +706,7 @@ AgcMeanLuminance::calculateNewEv(uint32_t constraintModeIndex,\n \t\treturn exposureModeHelper->splitExposure(10ms);\n \t}\n \n-\tdouble gain = estimateInitialGain();\n+\tdouble gain = estimateInitialGain(traits);\n \tgain = constraintClampGain(constraintModeIndex, yHist, gain);\n \n \t/*\ndiff --git a/src/ipa/libipa/agc_mean_luminance.h b/src/ipa/libipa/agc_mean_luminance.h\nindex 27e92b6fce..f4e1680ab5 100644\n--- a/src/ipa/libipa/agc_mean_luminance.h\n+++ b/src/ipa/libipa/agc_mean_luminance.h\n@@ -30,7 +30,7 @@ class AgcMeanLuminance\n {\n public:\n \tAgcMeanLuminance();\n-\tvirtual ~AgcMeanLuminance();\n+\t~AgcMeanLuminance();\n \n \tstruct AgcConstraint {\n \t\tenum class Bound {\n@@ -43,6 +43,11 @@ public:\n \t\tPwl yTarget;\n \t};\n \n+\tstruct Traits {\n+\t\tvirtual ~Traits() = default;\n+\t\tvirtual double estimateLuminance(double gain) const = 0;\n+\t};\n+\n \tvoid configure(utils::Duration lineDuration, const CameraSensorHelper *sensorHelper);\n \tint parseTuningData(const ValueNode &tuningData);\n \n@@ -76,7 +81,8 @@ public:\n \n \tstd::tuple<utils::Duration, double, double, double>\n \tcalculateNewEv(uint32_t constraintModeIndex, uint32_t exposureModeIndex,\n-\t\t const Histogram &yHist, utils::Duration effectiveExposureValue);\n+\t\t const Histogram &yHist, utils::Duration effectiveExposureValue,\n+\t\t const Traits &traits);\n \n \tdouble effectiveYTarget() const;\n \n@@ -86,13 +92,11 @@ public:\n \t}\n \n private:\n-\tvirtual double estimateLuminance(const double gain) const = 0;\n-\n \tint parseRelativeLuminanceTarget(const ValueNode &tuningData);\n \tint parseConstraint(const ValueNode &modeDict, int32_t id);\n \tint parseConstraintModes(const ValueNode &tuningData);\n \tint parseExposureModes(const ValueNode &tuningData);\n-\tdouble estimateInitialGain() const;\n+\tdouble estimateInitialGain(const Traits &traits) const;\n \tdouble constraintClampGain(uint32_t constraintModeIndex,\n \t\t\t\t const Histogram &hist,\n \t\t\t\t double gain);\ndiff --git a/src/ipa/mali-c55/algorithms/agc.cpp b/src/ipa/mali-c55/algorithms/agc.cpp\nindex 83bbf69385..6e091603f9 100644\n--- a/src/ipa/mali-c55/algorithms/agc.cpp\n+++ b/src/ipa/mali-c55/algorithms/agc.cpp\n@@ -127,20 +127,19 @@ void AgcStatistics::parseStatistics(const mali_c55_stats_buffer *stats)\n }\n \n Agc::Agc()\n-\t: AgcMeanLuminance()\n {\n }\n \n int Agc::init(IPAContext &context, const ValueNode &tuningData)\n {\n-\tint ret = parseTuningData(tuningData);\n+\tint ret = agc_.parseTuningData(tuningData);\n \tif (ret)\n \t\treturn ret;\n \n \tcontext.ctrlMap[&controls::AeEnable] = ControlInfo(false, true);\n \tcontext.ctrlMap[&controls::DigitalGain] = ControlInfo(\n \t\tkMinDigitalGain, kMaxDigitalGain, kMinDigitalGain);\n-\tcontext.ctrlMap.merge(controls());\n+\tcontext.ctrlMap.merge(agc_.controls());\n \n \treturn 0;\n }\n@@ -163,17 +162,17 @@ int Agc::configure(IPAContext &context,\n \tcontext.activeState.agc.manual.sensorGain = context.configuration.agc.minAnalogueGain;\n \tcontext.activeState.agc.manual.exposure = context.configuration.agc.defaultExposure;\n \tcontext.activeState.agc.manual.ispGain = kMinDigitalGain;\n-\tcontext.activeState.agc.constraintMode = constraintModes().begin()->first;\n-\tcontext.activeState.agc.exposureMode = exposureModeHelpers().begin()->first;\n+\tcontext.activeState.agc.constraintMode = agc_.constraintModes().begin()->first;\n+\tcontext.activeState.agc.exposureMode = agc_.exposureModeHelpers().begin()->first;\n \n \t/* \\todo Run this again when FrameDurationLimits is passed in */\n-\tsetLimits(context.configuration.agc.minShutterSpeed,\n-\t\t context.configuration.agc.maxShutterSpeed,\n-\t\t context.configuration.agc.minAnalogueGain,\n-\t\t context.configuration.agc.maxAnalogueGain,\n-\t\t {});\n+\tagc_.setLimits(context.configuration.agc.minShutterSpeed,\n+\t\t context.configuration.agc.maxShutterSpeed,\n+\t\t context.configuration.agc.minAnalogueGain,\n+\t\t context.configuration.agc.maxAnalogueGain,\n+\t\t {});\n \n-\tresetFrameCount();\n+\tagc_.resetFrameCount();\n \n \treturn 0;\n }\n@@ -320,14 +319,30 @@ void Agc::prepare(IPAContext &context, const uint32_t frame,\n \tfillWeightsArrayBuffer(params, MaliC55Blocks::AexpIhistWeights);\n }\n \n-double Agc::estimateLuminance(const double gain) const\n+namespace {\n+\n+class AgcTraits final : public AgcMeanLuminance::Traits\n {\n-\tdouble rAvg = statistics_.rHist.interQuantileMean(0, 1) * gain;\n-\tdouble gAvg = statistics_.gHist.interQuantileMean(0, 1) * gain;\n-\tdouble bAvg = statistics_.bHist.interQuantileMean(0, 1) * gain;\n-\tdouble yAvg = rec601LuminanceFromRGB({ { rAvg, gAvg, bAvg } });\n+public:\n+\tAgcTraits(const AgcStatistics &statistics)\n+\t\t: statistics_(statistics)\n+\t{\n+\t}\n+\n+\tdouble estimateLuminance(double gain) const override\n+\t{\n+\t\tdouble rAvg = statistics_.rHist.interQuantileMean(0, 1) * gain;\n+\t\tdouble gAvg = statistics_.gHist.interQuantileMean(0, 1) * gain;\n+\t\tdouble bAvg = statistics_.bHist.interQuantileMean(0, 1) * gain;\n+\t\tdouble yAvg = rec601LuminanceFromRGB({ { rAvg, gAvg, bAvg } });\n+\n+\t\treturn yAvg / kNumHistogramBins;\n+\t}\n+\n+private:\n+\tconst AgcStatistics &statistics_;\n+};\n \n-\treturn yAvg / kNumHistogramBins;\n }\n \n void Agc::process(IPAContext &context,\n@@ -359,13 +374,15 @@ void Agc::process(IPAContext &context,\n \tdouble totalGain = analogueGain * digitalGain;\n \tutils::Duration currentShutter = exposure * configuration.sensor.lineDuration;\n \tutils::Duration effectiveExposureValue = currentShutter * totalGain;\n+\tAgcTraits agcTraits(statistics_);\n+\n \n \tutils::Duration shutterTime;\n \tdouble aGain, qGain, dGain;\n \tstd::tie(shutterTime, aGain, qGain, dGain) =\n-\t\tcalculateNewEv(activeState.agc.constraintMode,\n-\t\t\t activeState.agc.exposureMode, statistics_.yHist,\n-\t\t\t effectiveExposureValue);\n+\t\tagc_.calculateNewEv(activeState.agc.constraintMode,\n+\t\t\t\t activeState.agc.exposureMode, statistics_.yHist,\n+\t\t\t\t effectiveExposureValue, agcTraits);\n \n \tUQ<5, 8> dGainQ = std::clamp(static_cast<float>(dGain),\n \t\t\t\t kMinDigitalGain,\ndiff --git a/src/ipa/mali-c55/algorithms/agc.h b/src/ipa/mali-c55/algorithms/agc.h\nindex ee913de2b2..e36378a2ac 100644\n--- a/src/ipa/mali-c55/algorithms/agc.h\n+++ b/src/ipa/mali-c55/algorithms/agc.h\n@@ -43,7 +43,7 @@ private:\n \tunsigned int bIndex_;\n };\n \n-class Agc : public Algorithm, public AgcMeanLuminance\n+class Agc : public Algorithm\n {\n public:\n \tAgc();\n@@ -64,7 +64,6 @@ public:\n \t\t ControlList &metadata) override;\n \n private:\n-\tdouble estimateLuminance(const double gain) const override;\n \tvoid fillGainParamBlock(IPAContext &context,\n \t\t\t\tIPAFrameContext &frameContext,\n \t\t\t\tMaliC55Params *params);\n@@ -72,6 +71,7 @@ private:\n \tvoid fillWeightsArrayBuffer(MaliC55Params *params, enum MaliC55Blocks type);\n \n \tAgcStatistics statistics_;\n+\tAgcMeanLuminance agc_;\n };\n \n } /* namespace ipa::mali_c55::algorithms */\ndiff --git a/src/ipa/rkisp1/algorithms/agc.cpp b/src/ipa/rkisp1/algorithms/agc.cpp\nindex aace46a8d0..540b64ff99 100644\n--- a/src/ipa/rkisp1/algorithms/agc.cpp\n+++ b/src/ipa/rkisp1/algorithms/agc.cpp\n@@ -138,7 +138,7 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData)\n {\n \tint ret;\n \n-\tret = parseTuningData(tuningData);\n+\tret = agc_.parseTuningData(tuningData);\n \tif (ret)\n \t\treturn ret;\n \n@@ -158,7 +158,7 @@ int Agc::init(IPAContext &context, const ValueNode &tuningData)\n \t/* \\todo Move this to the Camera class */\n \tcontext.ctrlMap[&controls::AeEnable] = ControlInfo(false, true, true);\n \tcontext.ctrlMap[&controls::ExposureValue] = ControlInfo(-8.0f, 8.0f, 0.0f);\n-\tcontext.ctrlMap.merge(controls());\n+\tcontext.ctrlMap.merge(agc_.controls());\n \n \treturn 0;\n }\n@@ -184,9 +184,9 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)\n \tcontext.activeState.agc.exposureValue = 0.0;\n \n \tcontext.activeState.agc.constraintMode =\n-\t\tstatic_cast<controls::AeConstraintModeEnum>(constraintModes().begin()->first);\n+\t\tstatic_cast<controls::AeConstraintModeEnum>(agc_.constraintModes().begin()->first);\n \tcontext.activeState.agc.exposureMode =\n-\t\tstatic_cast<controls::AeExposureModeEnum>(exposureModeHelpers().begin()->first);\n+\t\tstatic_cast<controls::AeExposureModeEnum>(agc_.exposureModeHelpers().begin()->first);\n \tcontext.activeState.agc.meteringMode =\n \t\tstatic_cast<controls::AeMeteringModeEnum>(meteringModes_.begin()->first);\n \n@@ -200,17 +200,16 @@ int Agc::configure(IPAContext &context, const IPACameraSensorInfo &configInfo)\n \tcontext.configuration.agc.measureWindow.h_size = configInfo.outputSize.width;\n \tcontext.configuration.agc.measureWindow.v_size = configInfo.outputSize.height;\n \n-\tAgcMeanLuminance::configure(context.configuration.sensor.lineDuration,\n-\t\t\t\t context.camHelper.get());\n+\tagc_.configure(context.configuration.sensor.lineDuration, context.camHelper.get());\n \n-\tsetLimits(context.configuration.sensor.minExposureTime,\n-\t\t context.configuration.sensor.maxExposureTime,\n-\t\t context.configuration.sensor.minAnalogueGain,\n-\t\t context.configuration.sensor.maxAnalogueGain, {});\n+\tagc_.setLimits(context.configuration.sensor.minExposureTime,\n+\t\t context.configuration.sensor.maxExposureTime,\n+\t\t context.configuration.sensor.minAnalogueGain,\n+\t\t context.configuration.sensor.maxAnalogueGain, {});\n \n-\tcontext.activeState.agc.automatic.yTarget = effectiveYTarget();\n+\tcontext.activeState.agc.automatic.yTarget = agc_.effectiveYTarget();\n \n-\tresetFrameCount();\n+\tagc_.resetFrameCount();\n \n \treturn 0;\n }\n@@ -457,47 +456,6 @@ void Agc::fillMetadata(IPAContext &context, IPAFrameContext &frameContext,\n \tmetadata.set(controls::ExposureValue, frameContext.agc.exposureValue);\n }\n \n-/**\n- * \\brief Estimate the relative luminance of the frame with a given gain\n- * \\param[in] gain The gain to apply to the frame\n- *\n- * This function estimates the average relative luminance of the frame that\n- * would be output by the sensor if an additional \\a gain was applied.\n- *\n- * The estimation is based on the AE statistics for the current frame. Y\n- * averages for all cells are first multiplied by the gain, and then saturated\n- * to approximate the sensor behaviour at high brightness values. The\n- * approximation is quite rough, as it doesn't take into account non-linearities\n- * when approaching saturation. In this case, saturating after the conversion to\n- * YUV doesn't take into account the fact that the R, G and B components\n- * contribute differently to the relative luminance.\n- *\n- * The values are normalized to the [0.0, 1.0] range, where 1.0 corresponds to a\n- * theoretical perfect reflector of 100% reference white.\n- *\n- * More detailed information can be found in:\n- * https://en.wikipedia.org/wiki/Relative_luminance\n- *\n- * \\return The relative luminance\n- */\n-double Agc::estimateLuminance(double gain) const\n-{\n-\tASSERT(expMeans_.size() == weights_.size());\n-\tdouble ySum = 0.0;\n-\tdouble wSum = 0.0;\n-\n-\t/* Sum the averages, saturated to 255. */\n-\tfor (unsigned i = 0; i < expMeans_.size(); i++) {\n-\t\tdouble w = weights_[i];\n-\t\tySum += std::min(expMeans_[i] * gain, 255.0) * w;\n-\t\twSum += w;\n-\t}\n-\n-\t/* \\todo Weight with the AWB gains */\n-\n-\treturn ySum / wSum / 255;\n-}\n-\n /**\n * \\brief Process frame duration and compute vblank\n * \\param[in] context The shared IPA context\n@@ -519,6 +477,64 @@ void Agc::processFrameDuration(IPAContext &context,\n \tframeContext.agc.frameDuration = (sensorInfo.outputSize.height + frameContext.agc.vblank) * lineDuration;\n }\n \n+namespace {\n+\n+class AgcTraits final : public AgcMeanLuminance::Traits\n+{\n+public:\n+\tAgcTraits(Span<const uint8_t> expMeans, Span<const uint8_t> weights)\n+\t\t: expMeans_(expMeans), weights_(weights)\n+\t{\n+\t}\n+\n+\t/**\n+\t * \\brief Estimate the relative luminance of the frame with a given gain\n+\t * \\param[in] gain The gain to apply to the frame\n+\t *\n+\t * This function estimates the average relative luminance of the frame that\n+\t * would be output by the sensor if an additional \\a gain was applied.\n+\t *\n+\t * The estimation is based on the AE statistics for the current frame. Y\n+\t * averages for all cells are first multiplied by the gain, and then saturated\n+\t * to approximate the sensor behaviour at high brightness values. The\n+\t * approximation is quite rough, as it doesn't take into account non-linearities\n+\t * when approaching saturation. In this case, saturating after the conversion to\n+\t * YUV doesn't take into account the fact that the R, G and B components\n+\t * contribute differently to the relative luminance.\n+\t *\n+\t * The values are normalized to the [0.0, 1.0] range, where 1.0 corresponds to a\n+\t * theoretical perfect reflector of 100% reference white.\n+\t *\n+\t * More detailed information can be found in:\n+\t * https://en.wikipedia.org/wiki/Relative_luminance\n+\t *\n+\t * \\return The relative luminance\n+\t */\n+\tdouble estimateLuminance(double gain) const override\n+\t{\n+\t\tASSERT(expMeans_.size() == weights_.size());\n+\t\tdouble ySum = 0.0;\n+\t\tdouble wSum = 0.0;\n+\n+\t\t/* Sum the averages, saturated to 255. */\n+\t\tfor (unsigned i = 0; i < expMeans_.size(); i++) {\n+\t\t\tdouble w = weights_[i];\n+\t\t\tySum += std::min(expMeans_[i] * gain, 255.0) * w;\n+\t\t\twSum += w;\n+\t\t}\n+\n+\t\t/* \\todo Weight with the AWB gains */\n+\n+\t\treturn ySum / wSum / 255;\n+\t}\n+\n+private:\n+\tSpan<const uint8_t> expMeans_;\n+\tSpan<const uint8_t> weights_;\n+};\n+\n+}\n+\n /**\n * \\brief Process RkISP1 statistics, and run AGC operations\n * \\param[in] context The shared IPA context\n@@ -559,13 +575,6 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \n \tconst rkisp1_cif_isp_stat *params = &stats->params;\n \n-\t/* The lower 4 bits are fractional and meant to be discarded. */\n-\tHistogram hist({ params->hist.hist_bins, context.hw.numHistogramBins },\n-\t\t [](uint32_t x) { return x >> 4; });\n-\texpMeans_ = { params->ae.exp_mean, context.hw.numAeCells };\n-\tstd::vector<uint8_t> &modeWeights = meteringModes_.at(frameContext.agc.meteringMode);\n-\tweights_ = { modeWeights.data(), modeWeights.size() };\n-\n \t/*\n \t * Set the AGC limits using the fixed exposure time and/or gain in\n \t * manual mode, or the sensor limits in auto mode.\n@@ -598,8 +607,8 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \tif (context.activeState.wdr.mode != controls::WdrOff)\n \t\tadditionalConstraints.push_back(context.activeState.wdr.constraint);\n \n-\tsetLimits(minExposureTime, maxExposureTime, minAnalogueGain, maxAnalogueGain,\n-\t\t std::move(additionalConstraints));\n+\tagc_.setLimits(minExposureTime, maxExposureTime, minAnalogueGain, maxAnalogueGain,\n+\t\t std::move(additionalConstraints));\n \n \t/*\n \t * The Agc algorithm needs to know the effective exposure value that was\n@@ -617,15 +626,23 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \tif (frameContext.compress.enable)\n \t\teffectiveExposureValue *= frameContext.agc.quantizationGain;\n \n-\tsetExposureCompensation(pow(2.0, frameContext.agc.exposureValue));\n-\tsetLux(frameContext.lux.lux);\n+\t/* The lower 4 bits are fractional and meant to be discarded. */\n+\tHistogram hist({ params->hist.hist_bins, context.hw.numHistogramBins },\n+\t\t [](uint32_t x) { return x >> 4; });\n+\tAgcTraits agcTraits{\n+\t\t{ params->ae.exp_mean, context.hw.numAeCells },\n+\t\tmeteringModes_.at(frameContext.agc.meteringMode),\n+\t};\n+\n+\tagc_.setExposureCompensation(pow(2.0, frameContext.agc.exposureValue));\n+\tagc_.setLux(frameContext.lux.lux);\n \n \tutils::Duration newExposureTime;\n \tdouble aGain, qGain, dGain;\n \tstd::tie(newExposureTime, aGain, qGain, dGain) =\n-\t\tcalculateNewEv(frameContext.agc.constraintMode,\n-\t\t\t frameContext.agc.exposureMode,\n-\t\t\t hist, effectiveExposureValue);\n+\t\tagc_.calculateNewEv(frameContext.agc.constraintMode,\n+\t\t\t\t frameContext.agc.exposureMode,\n+\t\t\t\t hist, effectiveExposureValue, agcTraits);\n \n \tLOG(RkISP1Agc, Debug)\n \t\t<< \"Divided up exposure time, analogue gain, quantization gain\"\n@@ -637,7 +654,7 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \tactiveState.agc.automatic.exposure = newExposureTime / lineDuration;\n \tactiveState.agc.automatic.gain = aGain;\n \tactiveState.agc.automatic.quantizationGain = qGain;\n-\tactiveState.agc.automatic.yTarget = effectiveYTarget();\n+\tactiveState.agc.automatic.yTarget = agc_.effectiveYTarget();\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@@ -646,7 +663,6 @@ void Agc::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n \t\t\t std::max(frameContext.agc.minFrameDuration, newExposureTime));\n \n \tfillMetadata(context, frameContext, metadata);\n-\texpMeans_ = {};\n }\n \n REGISTER_IPA_ALGORITHM(Agc, \"Agc\")\ndiff --git a/src/ipa/rkisp1/algorithms/agc.h b/src/ipa/rkisp1/algorithms/agc.h\nindex dec79f2f39..0527ca0d5f 100644\n--- a/src/ipa/rkisp1/algorithms/agc.h\n+++ b/src/ipa/rkisp1/algorithms/agc.h\n@@ -22,7 +22,7 @@ namespace libcamera {\n \n namespace ipa::rkisp1::algorithms {\n \n-class Agc : public Algorithm, public AgcMeanLuminance\n+class Agc : public Algorithm\n {\n public:\n \tAgc();\n@@ -49,15 +49,12 @@ private:\n \n \tvoid fillMetadata(IPAContext &context, IPAFrameContext &frameContext,\n \t\t\t ControlList &metadata);\n-\tdouble estimateLuminance(double gain) const override;\n \tvoid processFrameDuration(IPAContext &context,\n \t\t\t\t IPAFrameContext &frameContext,\n \t\t\t\t utils::Duration frameDuration);\n \n-\tSpan<const uint8_t> expMeans_;\n-\tSpan<const uint8_t> weights_;\n-\n \tstd::map<int32_t, std::vector<uint8_t>> meteringModes_;\n+\tAgcMeanLuminance agc_;\n };\n \n } /* namespace ipa::rkisp1::algorithms */\n", "prefixes": [ "RFC", "v1", "04/17" ] }