Patch Detail
Show a patch.
GET /api/1.1/patches/11347/?format=api
{ "id": 11347, "url": "https://patchwork.libcamera.org/api/1.1/patches/11347/?format=api", "web_url": "https://patchwork.libcamera.org/patch/11347/", "project": { "id": 1, "url": "https://patchwork.libcamera.org/api/1.1/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": "<20210219172224.69862-3-jeanmichel.hautbois@ideasonboard.com>", "date": "2021-02-19T17:22:24", "name": "[libcamera-devel,RFC,2/2] WIP: ipa: ipu3: Add support for IPU3 AWB algorithm", "commit_ref": null, "pull_url": null, "state": "superseded", "archived": false, "hash": "3a6082036d2dfde78b5d5435538e9774efd998c7", "submitter": { "id": 75, "url": "https://patchwork.libcamera.org/api/1.1/people/75/?format=api", "name": "Jean-Michel Hautbois", "email": "jeanmichel.hautbois@ideasonboard.com" }, "delegate": null, "mbox": "https://patchwork.libcamera.org/patch/11347/mbox/", "series": [ { "id": 1711, "url": "https://patchwork.libcamera.org/api/1.1/series/1711/?format=api", "web_url": "https://patchwork.libcamera.org/project/libcamera/list/?series=1711", "date": "2021-02-19T17:22:22", "name": "Implement IPA algorithms and demo with IPU3", "version": 1, "mbox": "https://patchwork.libcamera.org/series/1711/mbox/" } ], "comments": "https://patchwork.libcamera.org/api/patches/11347/comments/", "check": "pending", "checks": "https://patchwork.libcamera.org/api/patches/11347/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 3F285BD1F6\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 19 Feb 2021 17:22:30 +0000 (UTC)", "from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id CA138689DF;\n\tFri, 19 Feb 2021 18:22:29 +0100 (CET)", "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 4B218689D8\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 19 Feb 2021 18:22:27 +0100 (CET)", "from localhost.localdomain (unknown\n\t[IPv6:2a01:e0a:169:7140:e9be:2a73:b117:75b1])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id D97EF87A;\n\tFri, 19 Feb 2021 18:22:25 +0100 (CET)" ], "Authentication-Results": "lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"HHGHxFsy\"; dkim-atps=neutral", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1613755346;\n\tbh=9OpELQ7GaEpM9tyjfHtrsx7RcqHt8eE+fwDpIh2EmW8=;\n\th=From:To:Cc:Subject:Date:In-Reply-To:References:From;\n\tb=HHGHxFsyiTyo7mmQ7sqSYRi7Zx4L0bpXawa90doqi0Xt1mpmN5kgRkY9jtlsm7dSt\n\tEH4dKNnYq7zlxCKtC/vUYN0+r9JcE4T44b3dKYInlketDMfpv+6wxEbzzsbbsoBo8C\n\tX5v8vgNn4rJ9PFQomgQUqKNAA/4R0vLYBEF27iUM=", "From": "Jean-Michel Hautbois <jeanmichel.hautbois@ideasonboard.com>", "To": "libcamera-devel@lists.libcamera.org", "Date": "Fri, 19 Feb 2021 18:22:24 +0100", "Message-Id": "<20210219172224.69862-3-jeanmichel.hautbois@ideasonboard.com>", "X-Mailer": "git-send-email 2.27.0", "In-Reply-To": "<20210219172224.69862-1-jeanmichel.hautbois@ideasonboard.com>", "References": "<20210219172224.69862-1-jeanmichel.hautbois@ideasonboard.com>", "MIME-Version": "1.0", "Subject": "[libcamera-devel] [RFC PATCH 2/2] WIP: ipa: ipu3: Add support for\n\tIPU3 AWB algorithm", "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>", "Content-Type": "text/plain; charset=\"us-ascii\"", "Content-Transfer-Encoding": "7bit", "Errors-To": "libcamera-devel-bounces@lists.libcamera.org", "Sender": "\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>" }, "content": "Inherit from the AWBAlgorithm and AGCAlgorithm classes to implement\nbasic functions.\nWhile exposure is not locked, AWB is not calculated and corrected.\nOnce AWB is done, a color temperature is estimated and default CCM matrices\nare used.\nImplement a basic \"grey-world\" AWB algorithm just for demonstration purpose.\n\nSigned-off-by: Jean-Michel Hautbois <jeanmichel.hautbois@ideasonboard.com>\n---\n src/ipa/ipu3/ipu3.cpp | 31 ++++-\n src/ipa/ipu3/ipu3_agc.cpp | 195 +++++++++++++++++++++++++++\n src/ipa/ipu3/ipu3_agc.h | 96 +++++++++++++\n src/ipa/ipu3/ipu3_awb.cpp | 182 +++++++++++++++++++++++++\n src/ipa/ipu3/ipu3_awb.h | 130 ++++++++++++++++++\n src/ipa/ipu3/meson.build | 8 +-\n src/libcamera/pipeline/ipu3/ipu3.cpp | 1 +\n 7 files changed, 638 insertions(+), 5 deletions(-)\n create mode 100644 src/ipa/ipu3/ipu3_agc.cpp\n create mode 100644 src/ipa/ipu3/ipu3_agc.h\n create mode 100644 src/ipa/ipu3/ipu3_awb.cpp\n create mode 100644 src/ipa/ipu3/ipu3_awb.h", "diff": "diff --git a/src/ipa/ipu3/ipu3.cpp b/src/ipa/ipu3/ipu3.cpp\nindex b63e58be..c3859f39 100644\n--- a/src/ipa/ipu3/ipu3.cpp\n+++ b/src/ipa/ipu3/ipu3.cpp\n@@ -13,6 +13,7 @@\n \n #include <libcamera/buffer.h>\n #include <libcamera/control_ids.h>\n+#include <libcamera/ipa/ipa_controller.h>\n #include <libcamera/ipa/ipa_interface.h>\n #include <libcamera/ipa/ipa_module_info.h>\n #include <libcamera/ipa/ipu3_ipa_interface.h>\n@@ -21,6 +22,9 @@\n #include \"libcamera/internal/buffer.h\"\n #include \"libcamera/internal/log.h\"\n \n+#include \"ipu3_agc.h\"\n+#include \"ipu3_awb.h\"\n+\n namespace libcamera {\n \n LOG_DEFINE_CATEGORY(IPAIPU3)\n@@ -28,6 +32,9 @@ LOG_DEFINE_CATEGORY(IPAIPU3)\n class IPAIPU3 : public ipa::ipu3::IPAIPU3Interface\n {\n public:\n+\tIPAIPU3()\n+\t\t: controller_() {}\n+\n \tint init([[maybe_unused]] const IPASettings &settings) override\n \t{\n \t\treturn 0;\n@@ -60,6 +67,11 @@ private:\n \tuint32_t gain_;\n \tuint32_t minGain_;\n \tuint32_t maxGain_;\n+\n+\tIPAController controller_;\n+\tIPU3Awb *awbAlgo_;\n+\tIPU3Agc *agcAlgo_;\n+\tipu3_uapi_params params_;\n };\n \n void IPAIPU3::configure(const std::map<uint32_t, ControlInfoMap> &entityControls)\n@@ -83,11 +95,18 @@ void IPAIPU3::configure(const std::map<uint32_t, ControlInfoMap> &entityControls\n \n \tminExposure_ = std::max(itExp->second.min().get<int32_t>(), 1);\n \tmaxExposure_ = itExp->second.max().get<int32_t>();\n-\texposure_ = maxExposure_;\n+\texposure_ = 123;\n \n \tminGain_ = std::max(itGain->second.min().get<int32_t>(), 1);\n \tmaxGain_ = itGain->second.max().get<int32_t>();\n-\tgain_ = maxGain_;\n+\tgain_ = 1;\n+\n+\tawbAlgo_ = new IPU3Awb(&controller_);\n+\tawbAlgo_->Initialise(params_);\n+\tagcAlgo_ = new IPU3Agc(&controller_);\n+\n+\t/*\\todo not used yet... */\n+\tcontroller_.Initialise();\n \n \tsetControls(0);\n }\n@@ -162,10 +181,10 @@ void IPAIPU3::processControls([[maybe_unused]] unsigned int frame,\n void IPAIPU3::fillParams(unsigned int frame, ipu3_uapi_params *params)\n {\n \t/* Prepare parameters buffer. */\n-\tmemset(params, 0, sizeof(*params));\n+\tawbAlgo_->updateBNR(params_);\n+\tmemcpy(params, ¶ms_, sizeof(*params));\n \n \t/* \\todo Fill in parameters buffer. */\n-\n \tipa::ipu3::IPU3Action op;\n \top.op = ipa::ipu3::ActionParamFilled;\n \n@@ -179,6 +198,10 @@ void IPAIPU3::parseStatistics(unsigned int frame,\n \n \t/* \\todo React to statistics and update internal state machine. */\n \t/* \\todo Add meta-data information to ctrls. */\n+\tagcAlgo_->Process(stats, exposure_, gain_);\n+\tif (agcAlgo_->Converged())\n+\t\tawbAlgo_->calculateWBGains(Rectangle(250, 160, 800, 400), stats);\n+\tsetControls(frame);\n \n \tipa::ipu3::IPU3Action op;\n \top.op = ipa::ipu3::ActionMetadataReady;\ndiff --git a/src/ipa/ipu3/ipu3_agc.cpp b/src/ipa/ipu3/ipu3_agc.cpp\nnew file mode 100644\nindex 00000000..db591e33\n--- /dev/null\n+++ b/src/ipa/ipu3/ipu3_agc.cpp\n@@ -0,0 +1,195 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2019, Raspberry Pi (Trading) Limited\n+ *\n+ * agc.cpp - AGC/AEC control algorithm\n+ */\n+#include <iostream>\n+#include <numeric>\n+#include <unordered_map>\n+\n+#include \"libcamera/internal/log.h\"\n+\n+#include \"ipu3_agc.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(IPU3Agc)\n+\n+#define NAME \"ipu3.agc\"\n+\n+IPU3Agc::IPU3Agc(IPAController *controller)\n+\t: AgcAlgorithm(controller), frameCount_(0),\n+\t ev_(1.0), flicker_period_(0.0),\n+\t max_shutter_(0), fixed_shutter_(0),\n+\t fixed_analogue_gain_(0.0), algoConverged_(false)\n+{\n+}\n+\n+char const *IPU3Agc::Name() const\n+{\n+\treturn NAME;\n+}\n+\n+unsigned int IPU3Agc::GetConvergenceFrames() const\n+{\n+\treturn config_.convergence_frames;\n+}\n+\n+void IPU3Agc::SetEv(double ev)\n+{\n+\tev_ = ev;\n+}\n+\n+void IPU3Agc::SetFlickerPeriod(double flicker_period)\n+{\n+\tflicker_period_ = flicker_period;\n+}\n+\n+void IPU3Agc::SetMaxShutter(double max_shutter)\n+{\n+\tmax_shutter_ = max_shutter;\n+}\n+\n+void IPU3Agc::SetFixedShutter(double fixed_shutter)\n+{\n+\tfixed_shutter_ = fixed_shutter;\n+}\n+\n+void IPU3Agc::SetFixedAnalogueGain(double fixed_analogue_gain)\n+{\n+\tfixed_analogue_gain_ = fixed_analogue_gain;\n+}\n+\n+void IPU3Agc::SetMeteringMode(std::string const &metering_mode_name)\n+{\n+\tmetering_mode_name_ = metering_mode_name;\n+}\n+\n+void IPU3Agc::SetExposureMode(std::string const &exposure_mode_name)\n+{\n+\texposure_mode_name_ = exposure_mode_name;\n+}\n+\n+void IPU3Agc::Prepare() {}\n+\n+void IPU3Agc::Process() {}\n+\n+/* \\todo This function is taken from numerical recipes and calculates all moments\n+ * It needs to be rewritten properly and maybe in a \"math\" class ? */\n+void IPU3Agc::moments(std::unordered_map<uint32_t, uint32_t> &data, int n)\n+{\n+\tint j;\n+\tdouble ep = 0.0, s, p;\n+\tdouble ave, adev, sdev;\n+\tdouble var, skew, curt;\n+\n+\ts = 0.0;\n+\tfor (j = 1; j <= n; j++)\n+\t\ts += data[j];\n+\n+\tave = s / n;\n+\tadev = var = skew = curt = 0.0;\n+\n+\tfor (j = 1; j <= n; j++) {\n+\t\tadev += s = data[j] - (ave);\n+\t\tep += s;\n+\t\tvar += (p = s * s);\n+\t\tskew += (p *= s);\n+\t\tcurt += (p *= s);\n+\t}\n+\n+\tadev /= n;\n+\tvar = (var - ep * ep / n) / (n - 1);\n+\tsdev = std::sqrt(var);\n+\n+\tif (var) {\n+\t\tskew /= n * var * sdev;\n+\t\tcurt = curt / (n * var * var) - 3.0;\n+\t}\n+\tskew_ = skew;\n+}\n+\n+void IPU3Agc::processBrightness(const ipu3_uapi_stats_3a *stats)\n+{\n+\tbrightnessVec_.clear();\n+\n+\t/*\\todo Replace constant values with real BDS configuration */\n+\tfor (uint32_t j = 0; j < 45; j++) {\n+\t\tfor (uint32_t i = 0; i < 160 * 45 * 8; i += 8) {\n+\t\t\tuint8_t Gr = stats->awb_raw_buffer.meta_data[i];\n+\t\t\tuint8_t R = stats->awb_raw_buffer.meta_data[i + 1];\n+\t\t\tuint8_t B = stats->awb_raw_buffer.meta_data[i + 2];\n+\t\t\tuint8_t Gb = stats->awb_raw_buffer.meta_data[i + 3];\n+\t\t\tbrightnessVec_.push_back(static_cast<uint32_t>(0.299 * R + 0.587 * (Gr + Gb) / 2 + 0.114 * B));\n+\t\t}\n+\t}\n+\tstd::sort(brightnessVec_.begin(), brightnessVec_.end());\n+\n+\t/* \\todo create a class to generate histograms ! */\n+\tstd::unordered_map<uint32_t, uint32_t> hist;\n+\tfor (uint32_t const &val : brightnessVec_)\n+\t\thist[val]++;\n+\tmoments(hist, 256);\n+}\n+\n+void IPU3Agc::lockExposure(uint32_t &exposure, uint32_t &gain)\n+{\n+\t/* Algorithm initialization wait for first valid frames */\n+\t/* \\todo - have a number of frames given by DelayedControls ?\n+\t * - implement a function for IIR */\n+\tif (frameCount_ == 4) {\n+\t\tprevExposure_ = exposure;\n+\n+\t\tprevSkew_ = skew_;\n+\t\t/* \\tdo use configured values */\n+\t\texposure = 800;\n+\t\tgain = 8;\n+\t\tcurrentExposure_ = exposure;\n+\t} else if (frameCount_ == 8) {\n+\t\tcurrentSkew_ = skew_;\n+\t\texposure = ((currentExposure_ * prevSkew_) + (prevExposure_ * currentSkew_)) / (prevSkew_ + currentSkew_);\n+\t\tnextExposure_ = exposure;\n+\t\tlastFrame_ = frameCount_;\n+\t} else if ((frameCount_ >= 12) && (frameCount_ - lastFrame_ >= 4)) {\n+\t\tcurrentSkew_ = skew_;\n+\t\t/* \\todo properly calculate a gain */\n+\t\tif (frameCount_ == 12)\n+\t\t\tgain = ((8 * prevSkew_) + (1 * currentSkew_)) / (prevSkew_ + currentSkew_);\n+\n+\t\tif (currentSkew_ - prevSkew_ > 1) {\n+\t\t\t/* under exposed */\n+\t\t\tprevExposure_ = nextExposure_;\n+\t\t\texposure = ((currentExposure_ * prevSkew_) + (prevExposure_ * currentSkew_)) / (prevSkew_ + currentSkew_);\n+\t\t\tnextExposure_ = exposure;\n+\t\t} else if (currentSkew_ - prevSkew_ < -1) {\n+\t\t\t/* over exposed */\n+\t\t\tcurrentExposure_ = nextExposure_;\n+\t\t\texposure = ((currentExposure_ * prevSkew_) + (prevExposure_ * currentSkew_)) / (prevSkew_ + currentSkew_);\n+\t\t\tnextExposure_ = exposure;\n+\t\t} else {\n+\t\t\t/* we have converged */\n+\t\t\talgoConverged_ = true;\n+\t\t}\n+\t\tlastFrame_ = frameCount_;\n+\t\tprevSkew_ = currentSkew_;\n+\t}\n+}\n+\n+void IPU3Agc::Process(const ipu3_uapi_stats_3a *stats, uint32_t &exposure, uint32_t &gain)\n+{\n+\tprocessBrightness(stats);\n+\tif (!algoConverged_)\n+\t\tlockExposure(exposure, gain);\n+\telse {\n+\t\t/* Are we still well exposed ? */\n+\t\tif ((skew_ < 2) || (skew_ > 4))\n+\t\t\talgoConverged_ = false;\n+\t}\n+\tframeCount_++;\n+}\n+\n+bool IPU3Agc::Converged()\n+{\n+\treturn algoConverged_;\n+}\ndiff --git a/src/ipa/ipu3/ipu3_agc.h b/src/ipa/ipu3/ipu3_agc.h\nnew file mode 100644\nindex 00000000..9a69d628\n--- /dev/null\n+++ b/src/ipa/ipu3/ipu3_agc.h\n@@ -0,0 +1,96 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2019, Raspberry Pi (Trading) Limited\n+ *\n+ * ipu3_agc.h - AGC/AEC control algorithm\n+ */\n+#ifndef __LIBCAMERA_IPU3_AGC_H__\n+#define __LIBCAMERA_IPU3_AGC_H__\n+\n+#include <mutex>\n+#include <vector>\n+\n+#include <linux/intel-ipu3.h>\n+\n+#include <libcamera/geometry.h>\n+\n+#include <libcamera/ipa/agc_algorithm.h>\n+#include <libcamera/ipa/ipa_controller.h>\n+\n+#define AGC_STATS_SIZE 15\n+\n+namespace libcamera {\n+\n+struct AgcMeteringMode {\n+\tdouble weights[AGC_STATS_SIZE];\n+\tRectangle metering_region[AGC_STATS_SIZE];\n+};\n+\n+struct AgcExposureMode {\n+\tstd::vector<double> shutter;\n+\tstd::vector<double> gain;\n+};\n+\n+struct AgcConfig {\n+\tstd::map<std::string, AgcMeteringMode> metering_modes;\n+\tstd::map<std::string, AgcExposureMode> exposure_modes;\n+\tdouble speed;\n+\tuint16_t startup_frames;\n+\tunsigned int convergence_frames;\n+\tstd::string default_metering_mode;\n+\tstd::string default_exposure_mode;\n+\tdouble default_exposure_time;\n+\tdouble default_analogue_gain;\n+};\n+\n+class IPU3Agc : public AgcAlgorithm\n+{\n+public:\n+\tIPU3Agc(IPAController *IPAcontroller);\n+\tchar const *Name() const override;\n+\tunsigned int GetConvergenceFrames() const override;\n+\tvoid SetEv(double ev) override;\n+\tvoid SetFlickerPeriod(double flicker_period) override;\n+\tvoid SetMaxShutter(double max_shutter) override; // microseconds\n+\tvoid SetFixedShutter(double fixed_shutter) override; // microseconds\n+\tvoid SetFixedAnalogueGain(double fixed_analogue_gain) override;\n+\tvoid SetMeteringMode(std::string const &metering_mode_name) override;\n+\tvoid SetExposureMode(std::string const &exposure_mode_name) override;\n+\tvoid Prepare() override;\n+\tvoid Process() override;\n+\tvoid Process(const ipu3_uapi_stats_3a *stats, uint32_t &exposure, uint32_t &gain);\n+\tbool Converged();\n+\n+private:\n+\tvoid moments(std::unordered_map<uint32_t, uint32_t> &data, int n);\n+\tvoid processBrightness(const ipu3_uapi_stats_3a *stats);\n+\tvoid lockExposure(uint32_t &exposure, uint32_t &gain);\n+\n+\tAgcConfig config_;\n+\tstd::string metering_mode_name_;\n+\tstd::string exposure_mode_name_;\n+\tuint64_t frameCount_;\n+\tuint64_t lastFrame_;\n+\n+\t/* Vector of calculated brightness for each cell */\n+\tstd::vector<uint32_t> brightnessVec_;\n+\tdouble ev_;\n+\tdouble flicker_period_;\n+\tdouble max_shutter_;\n+\tdouble fixed_shutter_;\n+\tdouble fixed_analogue_gain_;\n+\n+\t/* Values for filtering */\n+\tuint32_t prevExposure_;\n+\tuint32_t currentExposure_;\n+\tuint32_t nextExposure_;\n+\n+\tdouble skew_;\n+\tdouble prevSkew_;\n+\tdouble currentSkew_;\n+\tbool algoConverged_;\n+};\n+\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_IPU3_AGC_H__ */\ndiff --git a/src/ipa/ipu3/ipu3_awb.cpp b/src/ipa/ipu3/ipu3_awb.cpp\nnew file mode 100644\nindex 00000000..21286e4c\n--- /dev/null\n+++ b/src/ipa/ipu3/ipu3_awb.cpp\n@@ -0,0 +1,182 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2019, Raspberry Pi (Trading) Limited\n+ *\n+ * ipu3_awb.cpp - AWB control algorithm\n+ */\n+#include <iostream>\n+#include <numeric>\n+#include <unordered_map>\n+\n+#include \"libcamera/internal/log.h\"\n+\n+#include \"ipu3_awb.h\"\n+\n+using namespace libcamera;\n+\n+LOG_DEFINE_CATEGORY(IPU3Awb)\n+\n+#define NAME \"ipu3.awb\"\n+\n+IPU3Awb::IPU3Awb(IPAController *controller)\n+\t: AwbAlgorithm(controller)\n+{\n+}\n+\n+IPU3Awb::~IPU3Awb()\n+{\n+}\n+\n+char const *IPU3Awb::Name() const\n+{\n+\treturn NAME;\n+}\n+\n+void IPU3Awb::Initialise() {}\n+\n+void IPU3Awb::Initialise(ipu3_uapi_params ¶ms)\n+{\n+\tmemset(¶ms, 0, sizeof(params));\n+\tmemset(wbGains_, 8192, sizeof(wbGains_));\n+\twbGains_[0] = 8192 * 0.8;\n+\twbGains_[3] = 8192 * 0.8;\n+\tparams.use.acc_awb = 1;\n+\t/*\\todo fill the grid calculated based on BDS configuration */\n+\tparams.acc_param.awb.config = imgu_css_awb_defaults;\n+\n+\tparams.use.acc_bnr = 1;\n+\tparams.acc_param.bnr = imgu_css_bnr_defaults;\n+\n+\tparams.use.acc_ccm = 1;\n+\tparams.acc_param.ccm = imgu_css_ccm_3800k;\n+\n+\tparams.use.acc_gamma = 1;\n+\tparams.acc_param.gamma.gc_ctrl.enable = 1;\n+\n+\tuint32_t a = (32 * 245) / (245 - 9);\n+\n+\tfor (uint32_t i = 0; i < 10; i++)\n+\t\tparams.acc_param.gamma.gc_lut.lut[i] = 0;\n+\tfor (uint32_t i = 10; i < 245; i++)\n+\t\tparams.acc_param.gamma.gc_lut.lut[i] = a * i + (0 - a * 9);\n+\tfor (uint32_t i = 245; i < 255; i++)\n+\t\tparams.acc_param.gamma.gc_lut.lut[i] = 32 * 245;\n+\n+\tframe_count_ = 0;\n+\talgoConverged_ = false;\n+}\n+\n+unsigned int IPU3Awb::GetConvergenceFrames() const\n+{\n+\t// If colour gains have been explicitly set, there is no convergence\n+\t// to happen, so no need to drop any frames - return zero.\n+\tif (manual_r_ && manual_b_)\n+\t\treturn 0;\n+\telse\n+\t\treturn config_.convergence_frames;\n+}\n+\n+void IPU3Awb::SetMode(std::string const &mode_name)\n+{\n+\tmode_name_ = mode_name;\n+}\n+\n+void IPU3Awb::SetManualGains(double manual_r, double manual_b)\n+{\n+\t// If any of these are 0.0, we swich back to auto.\n+\tmanual_r_ = manual_r;\n+\tmanual_b_ = manual_b;\n+}\n+\n+uint32_t IPU3Awb::estimateCCT(uint8_t R, uint8_t G, uint8_t B)\n+{\n+\tdouble X = (-0.14282) * (R) + (1.54924) * (G) + (-0.95641) * (B);\n+\tdouble Y = (-0.32466) * (R) + (1.57837) * (G) + (-0.73191) * (B);\n+\tdouble Z = (-0.68202) * (R) + (0.77073) * (G) + (0.56332) * (B);\n+\n+\tdouble x = X / (X + Y + Z);\n+\tdouble y = Y / (X + Y + Z);\n+\n+\tdouble n = (x - 0.3320) / (0.1858 - y);\n+\treturn static_cast<uint32_t>(449 * n * n * n + 3525 * n * n + 6823.3 * n + 5520.33);\n+}\n+\n+void IPU3Awb::calculateWBGains([[maybe_unused]] Rectangle roi, const ipu3_uapi_stats_3a *stats)\n+{\n+\tif (algoConverged_)\n+\t\treturn;\n+\n+\tstd::vector<uint32_t> R_v, Gr_v, Gb_v, B_v;\n+\tPoint topleft = roi.topLeft();\n+\tuint32_t startY = (topleft.y / 16) * 160 * 8;\n+\tuint32_t startX = (topleft.x / 8) * 8;\n+\tuint32_t endX = (startX + (roi.size().width / 8)) * 8;\n+\n+\tfor (uint32_t j = (topleft.y / 16); j < (topleft.y / 16) + (roi.size().height / 16); j++) {\n+\t\tfor (uint32_t i = startX + startY; i < endX + startY; i += 8) {\n+\t\t\tGr_v.push_back(stats->awb_raw_buffer.meta_data[i]);\n+\t\t\tR_v.push_back(stats->awb_raw_buffer.meta_data[i + 1]);\n+\t\t\tB_v.push_back(stats->awb_raw_buffer.meta_data[i + 2]);\n+\t\t\tGb_v.push_back(stats->awb_raw_buffer.meta_data[i + 3]);\n+\t\t}\n+\t}\n+\n+\tstd::sort(R_v.begin(), R_v.end());\n+\tstd::sort(Gr_v.begin(), Gr_v.end());\n+\tstd::sort(B_v.begin(), B_v.end());\n+\tstd::sort(Gb_v.begin(), Gb_v.end());\n+\n+\tdouble Grmed = Gr_v[Gr_v.size() / 2];\n+\tdouble Rmed = R_v[R_v.size() / 2];\n+\tdouble Bmed = B_v[B_v.size() / 2];\n+\tdouble Gbmed = Gb_v[Gb_v.size() / 2];\n+\n+\tdouble Rgain = Grmed / Rmed;\n+\tdouble Bgain = Gbmed / Bmed;\n+\tLOG(IPU3Awb, Debug) << \"max R, Gr, B, Gb: \"\n+\t\t\t << R_v.back() << \",\"\n+\t\t\t << Gr_v.back() << \",\"\n+\t\t\t << B_v.back() << \",\"\n+\t\t\t << Gb_v.back();\n+\ttint_ = ((Rmed / Grmed) + (Bmed / Gbmed)) / 2;\n+\n+\t/* \\todo Those are corrections when light is really low\n+\t * it should be taken into account by AGC somehow */\n+\tif ((Rgain >= 2) && (Bgain < 2)) {\n+\t\twbGains_[0] = 4096 * tint_;\n+\t\twbGains_[1] = 8192 * Rgain;\n+\t\twbGains_[2] = 4096 * Bgain;\n+\t\twbGains_[3] = 4096 * tint_;\n+\t} else if ((Bgain >= 2) && (Rgain < 2)) {\n+\t\twbGains_[0] = 4096 * tint_;\n+\t\twbGains_[1] = 4096 * Rgain;\n+\t\twbGains_[2] = 8192 * Bgain;\n+\t\twbGains_[3] = 4096 * tint_;\n+\t} else {\n+\t\twbGains_[0] = 8192 * tint_;\n+\t\twbGains_[1] = 8192 * Rgain;\n+\t\twbGains_[2] = 8192 * Bgain;\n+\t\twbGains_[3] = 8192 * tint_;\n+\t}\n+\n+\tframe_count_++;\n+\n+\tcct_ = estimateCCT(Rmed, (Grmed + Gbmed) / 2, Bmed);\n+\n+\talgoConverged_ = true;\n+}\n+\n+void IPU3Awb::updateBNR(ipu3_uapi_params ¶ms)\n+{\n+\tif (!algoConverged_)\n+\t\treturn;\n+\n+\tparams.acc_param.bnr.wb_gains.gr = wbGains_[0];\n+\tparams.acc_param.bnr.wb_gains.r = wbGains_[1];\n+\tparams.acc_param.bnr.wb_gains.b = wbGains_[2];\n+\tparams.acc_param.bnr.wb_gains.gb = wbGains_[3];\n+\tif (cct_ < 5500)\n+\t\tparams.acc_param.ccm = imgu_css_ccm_3800k;\n+\telse\n+\t\tparams.acc_param.ccm = imgu_css_ccm_6000k;\n+}\ndiff --git a/src/ipa/ipu3/ipu3_awb.h b/src/ipa/ipu3/ipu3_awb.h\nnew file mode 100644\nindex 00000000..06389020\n--- /dev/null\n+++ b/src/ipa/ipu3/ipu3_awb.h\n@@ -0,0 +1,130 @@\n+/* SPDX-License-Identifier: BSD-2-Clause */\n+/*\n+ * Copyright (C) 2019, Raspberry Pi (Trading) Limited\n+ *\n+ * awb.h - AWB control algorithm\n+ */\n+#ifndef __LIBCAMERA_IPU3_AWB_H__\n+#define __LIBCAMERA_IPU3_AWB_H__\n+\n+#include <linux/intel-ipu3.h>\n+\n+#include <libcamera/geometry.h>\n+\n+#include <libcamera/ipa/awb_algorithm.h>\n+#include <libcamera/ipa/ipa_controller.h>\n+\n+namespace libcamera {\n+\n+const struct ipu3_uapi_bnr_static_config imgu_css_bnr_defaults = {\n+\t{ 16, 16, 16, 16 },\t\t\t/* wb_gains */\n+\t{ 255, 255, 255, 255 },\t\t\t/* wb_gains_thr */\n+\t{ 0, 0, 8, 6, 0, 14 },\t\t\t/* thr_coeffs */\n+\t{ 0, 0, 0, 0 },\t\t\t\t/* thr_ctrl_shd */\n+\t{ -648, 0, -366, 0 },\t\t\t/* opt_center */\n+\t{\t\t\t\t\t/* lut */\n+\t\t{ 17, 23, 28, 32, 36, 39, 42, 45,\n+\t\t 48, 51, 53, 55, 58, 60, 62, 64,\n+\t\t 66, 68, 70, 72, 73, 75, 77, 78,\n+\t\t 80, 82, 83, 85, 86, 88, 89, 90 }\n+\t},\n+\t{ 4, 0, 1, 8, 0, 8, 0, 8, 0 },\t\t/* bp_ctrl */\n+\t{ 8, 4, 4, 0, 8, 0, 1, 1, 1, 1, 0 },\t/* dn_detect_ctrl */\n+\t1296,\n+\t{ 419904, 133956 },\n+};\n+\n+/* settings for Auto White Balance */\n+const struct ipu3_uapi_awb_config_s imgu_css_awb_defaults = {\n+\t8191,\n+\t8191,\n+\t8191,\n+\t8191 | /* rgbs_thr_gr/r/gb/b */\n+\t\tIPU3_UAPI_AWB_RGBS_THR_B_EN | IPU3_UAPI_AWB_RGBS_THR_B_INCL_SAT,\n+\t.grid = {\n+\t\t.width = 160,\n+\t\t.height = 45,\n+\t\t.block_width_log2 = 3,\n+\t\t.block_height_log2 = 4,\n+\t\t.x_start = 0,\n+\t\t.y_start = 0,\n+\t},\n+};\n+\n+const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_6000k = {\n+\t7239, -750, -37, 0,\n+\t-215, 9176, -200, 0,\n+\t-70, -589, 6810, 0\n+};\n+\n+const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_4900k = {\n+\t7811, -464, -466, 0,\n+\t-635, 8762, -533, 0,\n+\t-469, -154, 6583, 0\n+};\n+\n+const struct ipu3_uapi_ccm_mat_config imgu_css_ccm_3800k = {\n+\t7379, -526, -296, 0,\n+\t-411, 7397, -415, 0,\n+\t-224, -564, 7244, 0\n+};\n+\n+struct AwbConfig {\n+\t// Only repeat the AWB calculation every \"this many\" frames\n+\tuint16_t frame_period;\n+\t// number of initial frames for which speed taken as 1.0 (maximum)\n+\tuint16_t startup_frames;\n+\tunsigned int convergence_frames; // approx number of frames to converge\n+\tdouble speed; // IIR filter speed applied to algorithm results\n+};\n+\n+#if 0\n+typedef struct awb_public_set_item{\n+ unsigned char Gr_avg;\n+ unsigned char R_avg;\n+ unsigned char B_avg;\n+ unsigned char Gb_avg;\n+ unsigned char sat_ratio;\n+ unsigned char padding0; /**< Added the padding so that the public matches that private */\n+ unsigned char padding1; /**< Added the padding so that the public matches that private */\n+ unsigned char padding2; /**< Added the padding so that the public matches that private */\n+} awb_public_set_item_t;\n+#endif\n+\n+class IPU3Awb : public AwbAlgorithm\n+{\n+public:\n+\tIPU3Awb(IPAController *controller = NULL);\n+\t~IPU3Awb();\n+\tvirtual char const *Name() const override;\n+\tvirtual void Initialise() override;\n+\tvoid Initialise(ipu3_uapi_params ¶ms);\n+\tunsigned int GetConvergenceFrames() const override;\n+\tvoid SetMode(std::string const &name) override;\n+\tvoid SetManualGains(double manual_r, double manual_b) override;\n+\tvoid calculateWBGains(Rectangle roi,\n+\t\t\t const ipu3_uapi_stats_3a *stats);\n+\tvoid updateBNR(ipu3_uapi_params ¶ms);\n+\n+private:\n+\tuint32_t estimateCCT(uint8_t R, uint8_t G, uint8_t B);\n+\n+\t/* configuration is read-only, and available to both threads */\n+\tAwbConfig config_;\n+\tstd::string mode_name_;\n+\t/* manual r setting */\n+\tdouble manual_r_;\n+\t/* manual b setting */\n+\tdouble manual_b_;\n+\t/* WB calculated gains */\n+\tuint16_t wbGains_[4];\n+\tdouble tint_;\n+\tuint32_t cct_;\n+\n+\tuint32_t frame_count_;\n+\n+\tbool algoConverged_;\n+};\n+} /* namespace libcamera */\n+\n+#endif /* __LIBCAMERA_IPU3_AWB_H__ */\ndiff --git a/src/ipa/ipu3/meson.build b/src/ipa/ipu3/meson.build\nindex a241f617..43ad0e0d 100644\n--- a/src/ipa/ipu3/meson.build\n+++ b/src/ipa/ipu3/meson.build\n@@ -2,8 +2,14 @@\n \n ipa_name = 'ipa_ipu3'\n \n+ipu3_ipa_sources = files([\n+ 'ipu3.cpp',\n+ 'ipu3_agc.cpp',\n+ 'ipu3_awb.cpp',\n+])\n+\n mod = shared_module(ipa_name,\n- ['ipu3.cpp', libcamera_generated_ipa_headers],\n+ [ipu3_ipa_sources, libcamera_generated_ipa_headers],\n name_prefix : '',\n include_directories : [ipa_includes, libipa_includes],\n dependencies : libcamera_dep,\ndiff --git a/src/libcamera/pipeline/ipu3/ipu3.cpp b/src/libcamera/pipeline/ipu3/ipu3.cpp\nindex ff980b38..3809c943 100644\n--- a/src/libcamera/pipeline/ipu3/ipu3.cpp\n+++ b/src/libcamera/pipeline/ipu3/ipu3.cpp\n@@ -825,6 +825,7 @@ int PipelineHandlerIPU3::initControls(IPU3CameraData *data)\n \t */\n \tdouble lineDuration = sensorInfo.lineLength\n \t\t\t / (sensorInfo.pixelRate / 1e6);\n+\tLOG(IPU3, Error) << \"line duration: \" << lineDuration;\n \tconst ControlInfoMap &sensorControls = sensor->controls();\n \tconst ControlInfo &v4l2Exposure = sensorControls.find(V4L2_CID_EXPOSURE)->second;\n \tint32_t minExposure = v4l2Exposure.min().get<int32_t>() * lineDuration;\n", "prefixes": [ "libcamera-devel", "RFC", "2/2" ] }