Patch Detail
Show a patch.
GET /api/patches/27064/?format=api
{ "id": 27064, "url": "https://patchwork.libcamera.org/api/patches/27064/?format=api", "web_url": "https://patchwork.libcamera.org/patch/27064/", "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": "<20260626-ipu3-libipa-rework-v2-4-41546e23de3e@ideasonboard.com>", "date": "2026-06-26T13:05:51", "name": "[v2,04/12] ipa: ipu3: awb: Port to the new libipa AwbAlgorithm", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "0c113b543e5a43e6e20f43b9e6d7c729f0c5345d", "submitter": { "id": 156, "url": "https://patchwork.libcamera.org/api/people/156/?format=api", "name": "Dan Scally", "email": "dan.scally@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/27064/mbox/", "series": [ { "id": 6022, "url": "https://patchwork.libcamera.org/api/series/6022/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=6022", "date": "2026-06-26T13:05:47", "name": "libipa: Re-work IPU3 IPA to use libipa algorithms", "version": 2, "mbox": "https://patchwork.libcamera.org/series/6022/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/27064/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/27064/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 B02CBC3303\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 26 Jun 2026 13:06:18 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 8546B65EF2;\n\tFri, 26 Jun 2026 15:06:11 +0200 (CEST)", "from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id B94BB65EE7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 26 Jun 2026 15:06:05 +0200 (CEST)", "from [127.0.1.1]\n\t(chfd-03-b2-v4wan-176392-cust229.vm15.cable.virginm.net\n\t[82.19.20.230])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B95001ADF;\n\tFri, 26 Jun 2026 15:05:24 +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=\"aNtk72J9\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1782479125;\n\tbh=YYbJaObXm/0PNCPcdffs9U7nMV4aoqxvPj5rhy71Le4=;\n\th=From:Date:Subject:References:In-Reply-To:To:Cc:From;\n\tb=aNtk72J9jNDNOiYAATuARdgjcZ8XCWNOJzuRkEd0+fzvsvJktsZ1C+95KNCrZ9ihT\n\tEaSTZuxdz33T7si3OSSwx39Tcl1z8QLBLLzNze0mmgIQs++b0Q5nKQjhmw0wUbMlpf\n\tnWLRzJEqQE9ulJUj3/aBLhM/lzSaCgM4JdX01aVk=", "From": "Daniel Scally <dan.scally@ideasonboard.com>", "Date": "Fri, 26 Jun 2026 14:05:51 +0100", "Subject": "[PATCH v2 04/12] ipa: ipu3: awb: Port to the new libipa AwbAlgorithm", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-Id": "<20260626-ipu3-libipa-rework-v2-4-41546e23de3e@ideasonboard.com>", "References": "<20260626-ipu3-libipa-rework-v2-0-41546e23de3e@ideasonboard.com>", "In-Reply-To": "<20260626-ipu3-libipa-rework-v2-0-41546e23de3e@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Cc": "Daniel Scally <dan.scally@ideasonboard.com>, \n\tJacopo Mondi <jacopo.mondi@ideasonboard.com>, \n\tKieran Bingham <kieran.bingham@ideasonboard.com>", "X-Mailer": "b4 0.14.2", "X-Developer-Signature": "v=1; a=openpgp-sha256; l=17392;\n\ti=dan.scally@ideasonboard.com; h=from:subject:message-id;\n\tbh=YYbJaObXm/0PNCPcdffs9U7nMV4aoqxvPj5rhy71Le4=;\n\tb=owEBbQKS/ZANAwAKAchJV3psRXUyAcsmYgBqPnk6TMNgLOolQWCQYoR5zC5STIC3Ndp0Z/AJY\n\t8lFI+U2RV+JAjMEAAEKAB0WIQQqyuwyDnZdb+mxmm/ISVd6bEV1MgUCaj55OgAKCRDISVd6bEV1\n\tMkLrD/43B4yYPAFTdjq9wLMR3AqeJLE+GzniPBw15HWoQAsOjIRAaa3MzQ+bRJLAJtsoLBoR1qp\n\tfEnq1eaZw93A8aC2zPfxHIhQrFNBVQWYx9n3D5VW4POVsHYuD2ppc6MU80B/K1wqqjp74aUYAV7\n\tK5KVhWVoWnVsMYGVKj75HUF3D9pe3XxZeq7Oh+vv+zc+tEhdzdV8GzMHB8QgaiW0MIoM2RChqX6\n\tECa2v6DP07RTvYxGNMqrkEfkw1ZWAjSqhnhV4KCwvmejHagsbGeDtGiT9EHcLjS8NUe57VupkLC\n\tZGI5fKjWyY44Cs66ExfeyBFLJHmxPb3MMGhVGhPk/ElmowXoJSBk4N/Li/Jy/tj0tSIKgM8ukam\n\tI2cR+ZkChN15lbeUA6S5foKp4R5qttBKW/fx7oDmOz+0DAMhNjfTplpCcER41hfXrCvA4mHfeVx\n\tyysqFxjv52n2J0/TItR2G3Mbt9oWT7/Rf9NAeCoW7EKQfxQy/KQGxHBGsMD3Mc2bCb2+0Xa7vBl\n\tTqTOfWRapyVn1ZkwW3+fWDygUXmFZbwQt81mEC+pqJhBKWQZZgjtvIdPJKislCFL1ojq0V7Iwbg\n\tbFpAnTgtUewRws9EAcZECAHLHcwx51Sm563r2LiizavhHgQrU/bnnXasORJu4tIW4LQvJVRwet0\n\tTein9Q5Kao/trOQ==", "X-Developer-Key": "i=dan.scally@ideasonboard.com; a=openpgp;\n\tfpr=EEC699ACA1B7CB5D31330C0BBD501C2A3546CCF6", "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": "Port the IPU3 Awb algorithm to use the new libipa implementation\nof AwbAlgorithm.\n\nIn this implementation the awbAlgo_ class member is initialised as\nUQ<3, 13> following the IPU3 params format documentation.\n\nReviewed-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\nSigned-off-by: Daniel Scally <dan.scally@ideasonboard.com>\n---\nChanges in v2:\n\n - Used the new RGB<double>(gains_) member of the class\n\t- Fixed includes\n\t- Commented minColourValue\n---\n src/ipa/ipu3/algorithms/agc.cpp | 8 +-\n src/ipa/ipu3/algorithms/awb.cpp | 204 +++++++++++++++++++---------------------\n src/ipa/ipu3/algorithms/awb.h | 21 ++---\n src/ipa/ipu3/ipa_context.cpp | 30 ++----\n src/ipa/ipu3/ipa_context.h | 13 +--\n 5 files changed, 124 insertions(+), 152 deletions(-)", "diff": "diff --git a/src/ipa/ipu3/algorithms/agc.cpp b/src/ipa/ipu3/algorithms/agc.cpp\nindex b8b880b357a4770efd6810a3bdf616dd25ce93e4..de26e01ec901c2af109dbabd8fb84505a3107e82 100644\n--- a/src/ipa/ipu3/algorithms/agc.cpp\n+++ b/src/ipa/ipu3/algorithms/agc.cpp\n@@ -207,9 +207,11 @@ 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-\tgains_ = RGB<double>({ context.activeState.awb.gains.red,\n-\t\t\t context.activeState.awb.gains.blue,\n-\t\t\t context.activeState.awb.gains.green });\n+\n+\tif (context.activeState.awb.autoEnabled)\n+\t\tgains_ = context.activeState.awb.automatic.gains;\n+\telse\n+\t\tgains_ = context.activeState.awb.manual.gains;\n \n \t/*\n \t * The Agc algorithm needs to know the effective exposure value that was\ndiff --git a/src/ipa/ipu3/algorithms/awb.cpp b/src/ipa/ipu3/algorithms/awb.cpp\nindex 613bdbbf51cd127e03906571813260a6857067dc..be493659ea5780b6971fe3c4863c5a44b2114d26 100644\n--- a/src/ipa/ipu3/algorithms/awb.cpp\n+++ b/src/ipa/ipu3/algorithms/awb.cpp\n@@ -13,8 +13,6 @@\n \n #include <libcamera/control_ids.h>\n \n-#include \"libipa/colours.h\"\n-\n /**\n * \\file awb.h\n */\n@@ -26,11 +24,48 @@ namespace ipa::ipu3::algorithms {\n LOG_DEFINE_CATEGORY(IPU3Awb)\n \n /*\n- * When zones are used for the grey world algorithm, they are only considered if\n- * their average green value is at least 16/255 (after black level subtraction)\n- * to exclude zones that are too dark and don't provide relevant colour\n- * information (on the opposite side of the spectrum, saturated regions are\n- * excluded by the ImgU statistics engine).\n+ * \\todo IPU3 doesn't support the Lux algorithm.\n+ */\n+static constexpr unsigned int kDefaultLux = 500;\n+\n+/**\n+ * \\brief The IPU3 implementation of AwbStats\n+ */\n+class Ipu3AwbStats final : public AwbStats\n+{\n+public:\n+\tIpu3AwbStats(){};\n+\tIpu3AwbStats(const RGB<double> means)\n+\t\t: AwbStats(means)\n+\t{\n+\t}\n+\n+\t/*\n+\t * The minimum colour value is designed to allow libipa to make an\n+\t * assessment as to whether there's enough information in the statistics\n+\t * for a frame to be useful. The IPU3 implementation already drops any\n+\t * statistics zone with an average value below a threshold though so we\n+\t * don't need to do it in libipa. Set the threshold to zero so that all\n+\t * frames' statistics are counted as valid.\n+\t */\n+\tdouble minColourValue() const override\n+\t{\n+\t\treturn 0.0;\n+\t}\n+};\n+\n+/**\n+ * \\fn Ipu3AwbStats::Ipu3AwbStats(const RGB<double> means)\n+ * \\brief Construct an instance of the class with RGB means\n+ * \\param[in] means The mean R, G and B values from the statistics\n+ */\n+\n+/*\n+ * Zones are only considered if their average green value is at least\n+ * kMinGreenLevelInZone/255 (after black level subtraction) to exclude zones\n+ * that are too dark and don't provide relevant colour information (on the\n+ * opposite side of the spectrum, saturated regions are excluded by the ImgU\n+ * statistics engine).\n */\n static constexpr uint32_t kMinGreenLevelInZone = 16;\n \n@@ -74,26 +109,6 @@ static constexpr uint32_t kMinCellsPerZoneRatio = 255 * 90 / 100;\n * \\brief Sum of the average blue values of each unsaturated cell in the zone\n */\n \n-/**\n- * \\struct Awb::AwbStatus\n- * \\brief AWB parameters calculated\n- *\n- * The AwbStatus structure is intended to store the AWB\n- * parameters calculated by the algorithm\n- *\n- * \\var AwbStatus::temperatureK\n- * \\brief Color temperature calculated\n- *\n- * \\var AwbStatus::redGain\n- * \\brief Gain calculated for the red channel\n- *\n- * \\var AwbStatus::greenGain\n- * \\brief Gain calculated for the green channel\n- *\n- * \\var AwbStatus::blueGain\n- * \\brief Gain calculated for the blue channel\n- */\n-\n /* Default settings for Bayer noise reduction replicated from the Kernel */\n static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = {\n \t.wb_gains = { 16, 16, 16, 16 },\n@@ -114,11 +129,7 @@ static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = {\n \n /**\n * \\class Awb\n- * \\brief A Grey world white balance correction algorithm\n- *\n- * The Grey World algorithm assumes that the scene, in average, is neutral grey.\n- * Reference: Lam, Edmund & Fung, George. (2008). Automatic White Balancing in\n- * Digital Photography. 10.1201/9781420054538.ch10.\n+ * \\brief The IPU3 white balance correction algorithm implementation\n *\n * The IPU3 generates statistics from the Bayer Down Scaler output into a grid\n * defined in the ipu3_uapi_awb_config_s structure.\n@@ -168,26 +179,26 @@ static const struct ipu3_uapi_bnr_static_config imguCssBnrDefaults = {\n * cells are ignored. The grid configuration is computed by\n * IPAIPU3::calculateBdsGrid().\n *\n- * Before calculating the gains, the algorithm aggregates the cell averages for\n- * each zone in generateAwbStats(). Cells that have a too high ratio of\n- * saturated pixels are ignored, and only zones that contain enough\n- * non-saturated cells are then used by the algorithm.\n- *\n- * The Grey World algorithm will then estimate the red and blue gains to apply, and\n- * store the results in the metadata. The green gain is always set to 1.\n+ * Before running the AWB algorithm, we aggregate the cell averages for each\n+ * zone in generateAwbStats(). Cells that have a too high ratio of saturated\n+ * pixels are ignored, and only zones that contain enough non-saturated cells\n+ * are then used by the algorithm.\n */\n \n Awb::Awb()\n \t: Algorithm()\n {\n-\tasyncResults_.blueGain = 1.0;\n-\tasyncResults_.greenGain = 1.0;\n-\tasyncResults_.redGain = 1.0;\n-\tasyncResults_.temperatureK = 4500;\n-\n \tzones_.reserve(kAwbStatsSizeX * kAwbStatsSizeY);\n }\n \n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::init\n+ */\n+int Awb::init(IPAContext &context, const ValueNode &tuningData)\n+{\n+\treturn awbAlgo_.init(tuningData, context.ctrlMap);\n+}\n+\n /**\n * \\copydoc libcamera::ipa::Algorithm::configure\n */\n@@ -197,12 +208,14 @@ int Awb::configure(IPAContext &context,\n \tconst ipu3_uapi_grid_config &grid = context.configuration.grid.bdsGrid;\n \tstride_ = context.configuration.grid.stride;\n \n+\tawbAlgo_.configure(context.activeState.awb);\n+\n \tcellsPerZoneX_ = std::round(grid.width / static_cast<double>(kAwbStatsSizeX));\n \tcellsPerZoneY_ = std::round(grid.height / static_cast<double>(kAwbStatsSizeY));\n \n \t/*\n \t * Configure the minimum proportion of cells counted within a zone\n-\t * for it to be relevant for the grey world algorithm.\n+\t * for it to be used.\n \t * \\todo This proportion could be configured.\n \t */\n \tcellsPerZoneThreshold_ = cellsPerZoneX_ * cellsPerZoneY_ * kMaxCellSaturationRatio;\n@@ -211,6 +224,17 @@ int Awb::configure(IPAContext &context,\n \treturn 0;\n }\n \n+/**\n+ * \\copydoc libcamera::ipa::Algorithm::queueRequest\n+ */\n+void Awb::queueRequest(IPAContext &context, const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t const ControlList &controls)\n+{\n+\tawbAlgo_.queueRequest(context.activeState.awb, frame, frameContext.awb,\n+\t\t\t controls);\n+}\n+\n constexpr uint16_t Awb::threshold(float value)\n {\n \t/* AWB thresholds are in the range [0, 8191] */\n@@ -237,11 +261,12 @@ constexpr uint16_t Awb::gainValue(double gain)\n /**\n * \\copydoc libcamera::ipa::Algorithm::prepare\n */\n-void Awb::prepare(IPAContext &context,\n-\t\t [[maybe_unused]] const uint32_t frame,\n-\t\t [[maybe_unused]] IPAFrameContext &frameContext,\n-\t\t ipu3_uapi_params *params)\n+void Awb::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n+\t\t IPAFrameContext &frameContext,\n+\t\t [[maybe_unused]] ipu3_uapi_params *params)\n {\n+\tawbAlgo_.prepare(context.activeState.awb, frameContext.awb);\n+\n \t/*\n \t * Green saturation thresholds are reduced because we are using the\n \t * green channel only in the exposure computation.\n@@ -279,13 +304,11 @@ void Awb::prepare(IPAContext &context,\n \tparams->acc_param.bnr.opt_center_sqr.y_sqr_reset = params->acc_param.bnr.opt_center.y_reset\n \t\t\t\t\t\t\t* params->acc_param.bnr.opt_center.y_reset;\n \n-\tparams->acc_param.bnr.wb_gains.gr = gainValue(context.activeState.awb.gains.green);\n-\tparams->acc_param.bnr.wb_gains.r = gainValue(context.activeState.awb.gains.red);\n-\tparams->acc_param.bnr.wb_gains.b = gainValue(context.activeState.awb.gains.blue);\n-\tparams->acc_param.bnr.wb_gains.gb = gainValue(context.activeState.awb.gains.green);\n-\n-\tLOG(IPU3Awb, Debug) << \"Color temperature estimated: \" << asyncResults_.temperatureK;\n \n+\tparams->acc_param.bnr.wb_gains.gr = gainValue(frameContext.awb.gains.g());\n+\tparams->acc_param.bnr.wb_gains.r = gainValue(frameContext.awb.gains.r());\n+\tparams->acc_param.bnr.wb_gains.b = gainValue(frameContext.awb.gains.b());\n+\tparams->acc_param.bnr.wb_gains.gb = gainValue(frameContext.awb.gains.g());\n \n \tparams->use.acc_awb = 1;\n \tparams->use.acc_bnr = 1;\n@@ -366,9 +389,17 @@ void Awb::clearAwbStats()\n \t}\n }\n \n-void Awb::awbGreyWorld()\n+Ipu3AwbStats Awb::calculateRgbMeans(const ipu3_uapi_stats_3a *stats)\n {\n-\tLOG(IPU3Awb, Debug) << \"Grey world AWB\";\n+\tASSERT(stats->stats_3a_status.awb_en);\n+\n+\tclearAwbStats();\n+\tgenerateAwbStats(stats);\n+\tgenerateZones();\n+\n+\tif (zones_.size() <= 10)\n+\t\treturn {};\n+\n \t/*\n \t * Make a separate list of the derivatives for each of red and blue, so\n \t * that we can sort them to exclude the extreme gains. We could\n@@ -399,66 +430,21 @@ void Awb::awbGreyWorld()\n \tdouble redGain = sumRed.g() / (sumRed.r() + 1),\n \t blueGain = sumBlue.g() / (sumBlue.b() + 1);\n \n-\t/* Color temperature is not relevant in Grey world but still useful to estimate it :-) */\n-\tasyncResults_.temperatureK = estimateCCT({{ sumRed.r(), sumRed.g(), sumBlue.b() }});\n-\n-\t/*\n-\t * Gain values are unsigned integer value ranging [0, 8) with 13 bit\n-\t * fractional part.\n-\t */\n-\tredGain = std::clamp(redGain, 0.0, 65535.0 / 8192);\n-\tblueGain = std::clamp(blueGain, 0.0, 65535.0 / 8192);\n-\n-\tasyncResults_.redGain = redGain;\n-\t/* Hardcode the green gain to 1.0. */\n-\tasyncResults_.greenGain = 1.0;\n-\tasyncResults_.blueGain = blueGain;\n-}\n-\n-void Awb::calculateWBGains(const ipu3_uapi_stats_3a *stats)\n-{\n-\tASSERT(stats->stats_3a_status.awb_en);\n-\n-\tclearAwbStats();\n-\tgenerateAwbStats(stats);\n-\tgenerateZones();\n-\n-\tLOG(IPU3Awb, Debug) << \"Valid zones: \" << zones_.size();\n-\n-\tif (zones_.size() > 10) {\n-\t\tawbGreyWorld();\n-\t\tLOG(IPU3Awb, Debug) << \"Gain found for red: \" << asyncResults_.redGain\n-\t\t\t\t << \" and for blue: \" << asyncResults_.blueGain;\n-\t}\n+\treturn Ipu3AwbStats({ { 1.0 / redGain, 1.0, 1.0 / blueGain } });\n }\n \n /**\n * \\copydoc libcamera::ipa::Algorithm::process\n */\n void Awb::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n-\t\t [[maybe_unused]] IPAFrameContext &frameContext,\n-\t\t const ipu3_uapi_stats_3a *stats,\n-\t\t [[maybe_unused]] ControlList &metadata)\n+\t\t IPAFrameContext &frameContext,\n+\t\t [[maybe_unused]] const ipu3_uapi_stats_3a *stats,\n+\t\t ControlList &metadata)\n {\n-\tcalculateWBGains(stats);\n+\tIpu3AwbStats awbStats = calculateRgbMeans(stats);\n \n-\t/*\n-\t * Gains are only recalculated if enough zones were detected.\n-\t * The results are cached, so if no results were calculated, we set the\n-\t * cached values from asyncResults_ here.\n-\t */\n-\tcontext.activeState.awb.gains.blue = asyncResults_.blueGain;\n-\tcontext.activeState.awb.gains.green = asyncResults_.greenGain;\n-\tcontext.activeState.awb.gains.red = asyncResults_.redGain;\n-\tcontext.activeState.awb.temperatureK = asyncResults_.temperatureK;\n-\n-\tmetadata.set(controls::AwbEnable, true);\n-\tmetadata.set(controls::ColourGains, {\n-\t\t\tstatic_cast<float>(context.activeState.awb.gains.red),\n-\t\t\tstatic_cast<float>(context.activeState.awb.gains.blue)\n-\t\t});\n-\tmetadata.set(controls::ColourTemperature,\n-\t\t context.activeState.awb.temperatureK);\n+\tawbAlgo_.process(context.activeState.awb, frameContext.awb, awbStats,\n+\t\t\t kDefaultLux, metadata);\n }\n \n REGISTER_IPA_ALGORITHM(Awb, \"Awb\")\ndiff --git a/src/ipa/ipu3/algorithms/awb.h b/src/ipa/ipu3/algorithms/awb.h\nindex 7ec322318dab54ae7c8a647a67a0cf5815a36eb6..e1a7b2139e5dafa748b908230aa3189f33b09ced 100644\n--- a/src/ipa/ipu3/algorithms/awb.h\n+++ b/src/ipa/ipu3/algorithms/awb.h\n@@ -13,7 +13,8 @@\n \n #include <libcamera/geometry.h>\n \n-#include \"libcamera/internal/vector.h\"\n+#include \"libipa/awb.h\"\n+#include \"libipa/fixedpoint.h\"\n \n #include \"algorithm.h\"\n \n@@ -21,6 +22,8 @@ namespace libcamera {\n \n namespace ipa::ipu3::algorithms {\n \n+class Ipu3AwbStats;\n+\n /* Region size for the statistics generation algorithm */\n static constexpr uint32_t kAwbStatsSizeX = 16;\n static constexpr uint32_t kAwbStatsSizeY = 12;\n@@ -39,7 +42,11 @@ class Awb : public Algorithm\n public:\n \tAwb();\n \n+\tint init(IPAContext &context, const ValueNode &tuningData) override;\n \tint configure(IPAContext &context, const IPAConfigInfo &configInfo) override;\n+\tvoid queueRequest(IPAContext &context, const uint32_t frame,\n+\t\t\t IPAFrameContext &frameContext,\n+\t\t\t const ControlList &controls) override;\n \tvoid prepare(IPAContext &context, const uint32_t frame,\n \t\t IPAFrameContext &frameContext,\n \t\t ipu3_uapi_params *params) override;\n@@ -49,15 +56,7 @@ public:\n \t\t ControlList &metadata) override;\n \n private:\n-\tstruct AwbStatus {\n-\t\tdouble temperatureK;\n-\t\tdouble redGain;\n-\t\tdouble greenGain;\n-\t\tdouble blueGain;\n-\t};\n-\n-private:\n-\tvoid calculateWBGains(const ipu3_uapi_stats_3a *stats);\n+\tIpu3AwbStats calculateRgbMeans(const ipu3_uapi_stats_3a *stats);\n \tvoid generateZones();\n \tvoid generateAwbStats(const ipu3_uapi_stats_3a *stats);\n \tvoid clearAwbStats();\n@@ -67,7 +66,7 @@ private:\n \n \tstd::vector<RGB<double>> zones_;\n \tAccumulator awbStats_[kAwbStatsSizeX * kAwbStatsSizeY];\n-\tAwbStatus asyncResults_;\n+\tAwbAlgorithm<UQ<3, 13>> awbAlgo_;\n \n \tuint32_t stride_;\n \tuint32_t cellsPerZoneX_;\ndiff --git a/src/ipa/ipu3/ipa_context.cpp b/src/ipa/ipu3/ipa_context.cpp\nindex 3b22f7917650d9e400d5368c2f890d6b2dc846a0..e33c925f42b87887467a32dd46a42248a8723415 100644\n--- a/src/ipa/ipu3/ipa_context.cpp\n+++ b/src/ipa/ipu3/ipa_context.cpp\n@@ -109,6 +109,11 @@ namespace libcamera::ipa::ipu3 {\n * \\brief Maximum analogue gain supported with the configured sensor\n */\n \n+/**\n+ * \\var IPAActiveState::awb\n+ * \\brief Active auto-white balance parameters for the IPA\n+ */\n+\n /**\n * \\var IPASessionConfiguration::sensor\n * \\brief Sensor-specific configuration of the IPA\n@@ -139,26 +144,6 @@ namespace libcamera::ipa::ipu3 {\n * The gain should be adapted to the sensor specific gain code before applying.\n */\n \n-/**\n- * \\var IPAActiveState::awb\n- * \\brief Context for the Automatic White Balance algorithm\n- *\n- * \\var IPAActiveState::awb.gains\n- * \\brief White balance gains\n- *\n- * \\var IPAActiveState::awb.gains.red\n- * \\brief White balance gain for R channel\n- *\n- * \\var IPAActiveState::awb.gains.green\n- * \\brief White balance gain for G channel\n- *\n- * \\var IPAActiveState::awb.gains.blue\n- * \\brief White balance gain for B channel\n- *\n- * \\var IPAActiveState::awb.temperatureK\n- * \\brief Estimated color temperature\n- */\n-\n /**\n * \\var IPAActiveState::toneMapping\n * \\brief Context for ToneMapping and Gamma control\n@@ -187,4 +172,9 @@ namespace libcamera::ipa::ipu3 {\n * \\brief Analogue gain multiplier\n */\n \n+/**\n+ * \\var IPAFrameContext::awb\n+ * \\brief Per-frame auto-white balance parameters for the IPA\n+ */\n+\n } /* namespace libcamera::ipa::ipu3 */\ndiff --git a/src/ipa/ipu3/ipa_context.h b/src/ipa/ipu3/ipa_context.h\nindex 97fcf06cd4ac9ac6d64c4933fcea80ace0e572df..564cedde51d9e20263c4cc2767853d2a5f02d2e1 100644\n--- a/src/ipa/ipu3/ipa_context.h\n+++ b/src/ipa/ipu3/ipa_context.h\n@@ -15,6 +15,7 @@\n #include <libcamera/controls.h>\n #include <libcamera/geometry.h>\n \n+#include <libipa/awb.h>\n #include <libipa/fc_queue.h>\n \n namespace libcamera {\n@@ -60,15 +61,7 @@ struct IPAActiveState {\n \t\tuint32_t exposureMode;\n \t} agc;\n \n-\tstruct {\n-\t\tstruct {\n-\t\t\tdouble red;\n-\t\t\tdouble green;\n-\t\t\tdouble blue;\n-\t\t} gains;\n-\n-\t\tdouble temperatureK;\n-\t} awb;\n+\tipa::awb::ActiveState awb;\n \n \tstruct {\n \t\tdouble gamma;\n@@ -81,6 +74,8 @@ struct IPAFrameContext : public FrameContext {\n \t\tuint32_t exposure;\n \t\tdouble gain;\n \t} sensor;\n+\n+\tipa::awb::FrameContext awb;\n };\n \n struct IPAContext {\n", "prefixes": [ "v2", "04/12" ] }