[{"id":32898,"web_url":"https://patchwork.libcamera.org/comment/32898/","msgid":"<173469053598.547566.6604736916896487019@ping.linuxembedded.co.uk>","date":"2024-12-20T10:28:55","subject":"Re: [PATCH v3 1/2] ipa: libipa: Add Lux helper","submitter":{"id":4,"url":"https://patchwork.libcamera.org/api/people/4/","name":"Kieran Bingham","email":"kieran.bingham@ideasonboard.com"},"content":"Quoting Paul Elder (2024-12-18 07:46:00)\n> Add a Lux helper to libipa that does the estimation of the lux level\n> given gain, exposure, and luminance histogram. The helper also\n> handles reading the reference values from the tuning file. These are\n> expected to be common operations of lux algorithm modules in IPAs, and\n> is modeled/copied from Raspberry Pi.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n\nLooking forward to being able to report more helpful metadata to the\napplciations about the image!\n\nReviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n\n> \n> ---\n> Changes in v3:\n> - improve documentation\n> - minor docs formatting fixes\n> - replace setBinSize() with constructor parameter\n> - s/readYaml/parseTuningData/\n> - remove unnecessary includes\n> \n> Changes in v2:\n> - improve documentation\n> - add binSize member variable and corresponding setter\n> - remove aperture\n> - split gain into analogue and digital\n> - change tuning file names into camel case\n> ---\n>  src/ipa/libipa/lux.cpp     | 181 +++++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/lux.h       |  42 +++++++++\n>  src/ipa/libipa/meson.build |   2 +\n>  3 files changed, 225 insertions(+)\n>  create mode 100644 src/ipa/libipa/lux.cpp\n>  create mode 100644 src/ipa/libipa/lux.h\n> \n> diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp\n> new file mode 100644\n> index 000000000000..b60060e21803\n> --- /dev/null\n> +++ b/src/ipa/libipa/lux.cpp\n> @@ -0,0 +1,181 @@\n> +/* SPDX-License-Identifier: BSD-2-Clause */\n> +/*\n> + * Copyright (C) 2019, Raspberry Pi Ltd\n> + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>\n> + *\n> + * Helper class that implements lux estimation\n> + */\n> +#include \"lux.h\"\n> +\n> +#include <algorithm>\n> +#include <chrono>\n> +\n> +#include <libcamera/base/log.h>\n> +\n> +#include \"libcamera/internal/yaml_parser.h\"\n> +\n> +#include \"histogram.h\"\n> +\n> +/**\n> + * \\file lux.h\n> + * \\brief Helper class that implements lux estimation\n> + *\n> + * Estimating the lux level of an image is a common operation that can for\n> + * instance be used to adjust the target Y value in AGC or for Bayesian AWB\n> + * estimation.\n> + */\n> +\n> +namespace libcamera {\n> +\n> +using namespace std::literals::chrono_literals;\n> +\n> +LOG_DEFINE_CATEGORY(Lux)\n> +\n> +namespace ipa {\n> +\n> +/**\n> + * \\class Lux\n> + * \\brief Class that implements lux estimation\n> + *\n> + * IPAs that wish to use lux esimation should create a Lux algorithm module\n> + * that lightly wraps this module by providing the platform-specific luminance\n> + * histogram. The Lux entry in the tuning file must then precede the algorithms\n> + * that depend on the estimated lux value.\n> + */\n> +\n> +/**\n> + * \\var Lux::binSize_\n> + * \\brief The maximum count of each bin\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceExposureTime_\n> + * \\brief The exposure time of the reference image, in microseconds\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceAnalogueGain_\n> + * \\brief The analogue gain of the reference image\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceDigitalGain_\n> + * \\brief The analogue gain of the reference image\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceY_\n> + * \\brief The measured luminance of the reference image, out of the bin size\n> + *\n> + * \\sa binSize_\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceLux_\n> + * \\brief The estimated lux level of the reference image\n> + */\n> +\n> +/**\n> +  * \\brief Construct the Lux helper module\n> +  * \\param[in] binSize The maximum count of each bin\n> +  */\n> +Lux::Lux(unsigned int binSize)\n> +       : binSize_(binSize)\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Parse tuning data\n> + * \\param[in] tuningData The YamlObject representing the tuning data\n> + *\n> + * This function parses yaml tuning data for the common Lux module. It requires\n> + * reference exposure time, analogue gain, digital gain, and lux values.\n> + *\n> + * \\code{.unparsed}\n> + * algorithms:\n> + *   - Lux:\n> + *       referenceExposureTime: 10000\n> + *       referenceAnalogueGain: 4.0\n> + *       referenceDigitalGain: 1.0\n> + *       referenceY: 12000\n> + *       referenceLux: 1000\n> + * \\endcode\n> + *\n> + * \\return 0 on success or a negative error code\n> + */\n> +int Lux::parseTuningData(const YamlObject &tuningData)\n> +{\n> +       auto value = tuningData[\"referenceExposureTime\"].get<double>();\n> +       if (!value) {\n> +               LOG(Lux, Error) << \"Missing tuning parameter: \"\n> +                               << \"'referenceExposureTime'\";\n> +               return -EINVAL;\n> +       }\n> +       referenceExposureTime_ = *value * 1.0us;\n> +\n> +       value = tuningData[\"referenceAnalogueGain\"].get<double>();\n> +       if (!value) {\n> +               LOG(Lux, Error) << \"Missing tuning parameter: \"\n> +                               << \"'referenceAnalogueGain'\";\n> +               return -EINVAL;\n> +       }\n> +       referenceAnalogueGain_ = *value;\n> +\n> +       value = tuningData[\"referenceDigitalGain\"].get<double>();\n> +       if (!value) {\n> +               LOG(Lux, Error) << \"Missing tuning parameter: \"\n> +                               << \"'referenceDigitalGain'\";\n> +               return -EINVAL;\n> +       }\n> +       referenceDigitalGain_ = *value;\n> +\n> +       value = tuningData[\"referenceY\"].get<double>();\n> +       if (!value) {\n> +               LOG(Lux, Error) << \"Missing tuning parameter: \"\n> +                               << \"'referenceY'\";\n> +               return -EINVAL;\n> +       }\n> +       referenceY_ = *value;\n> +\n> +       value = tuningData[\"referenceLux\"].get<double>();\n> +       if (!value) {\n> +               LOG(Lux, Error) << \"Missing tuning parameter: \"\n> +                               << \"'referenceLux'\";\n> +               return -EINVAL;\n> +       }\n> +       referenceLux_ = *value;\n> +\n> +       return 0;\n> +}\n> +\n> +/**\n> + * \\brief Estimate lux given runtime values\n> + * \\param[in] exposureTime Exposure time applied to the frame\n> + * \\param[in] aGain Analogue gain applied to the frame\n> + * \\param[in] dGain Digital gain applied to the frame\n> + * \\param[in] yHist Histogram from the ISP statistics\n> + *\n> + * Estimate the lux given the exposure time, gain, and histogram.\n> + *\n> + * \\return Estimated lux value\n> + */\n> +double Lux::estimateLux(utils::Duration exposureTime,\n> +                       double aGain, double dGain,\n> +                       const Histogram &yHist) const\n> +{\n> +       double currentY = yHist.interQuantileMean(0, 1);\n> +       double exposureTimeRatio = referenceExposureTime_ / exposureTime;\n> +       double aGainRatio = referenceAnalogueGain_ / aGain;\n> +       double dGainRatio = referenceDigitalGain_ / dGain;\n> +       double yRatio = currentY * (binSize_ / yHist.bins()) / referenceY_;\n> +\n> +       double estimatedLux = exposureTimeRatio * aGainRatio * dGainRatio *\n> +                             yRatio * referenceLux_;\n> +\n> +       LOG(Lux, Debug) << \"Estimated lux \" << estimatedLux;\n> +       return estimatedLux;\n> +}\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/lux.h b/src/ipa/libipa/lux.h\n> new file mode 100644\n> index 000000000000..93ca64795803\n> --- /dev/null\n> +++ b/src/ipa/libipa/lux.h\n> @@ -0,0 +1,42 @@\n> +/* SPDX-License-Identifier: BSD-2-Clause */\n> +/*\n> + * Copyright (C) 2019, Raspberry Pi Ltd\n> + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>\n> + *\n> + * Helper class that implements lux estimation\n> + */\n> +\n> +#pragma once\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +namespace libcamera {\n> +\n> +class YamlObject;\n> +\n> +namespace ipa {\n> +\n> +class Histogram;\n> +\n> +class Lux\n> +{\n> +public:\n> +       Lux(unsigned int binSize);\n> +\n> +       int parseTuningData(const YamlObject &tuningData);\n> +       double estimateLux(utils::Duration exposureTime,\n> +                          double aGain, double dGain,\n> +                          const Histogram &yHist) const;\n> +\n> +private:\n> +       unsigned int binSize_;\n> +       utils::Duration referenceExposureTime_;\n> +       double referenceAnalogueGain_;\n> +       double referenceDigitalGain_;\n> +       double referenceY_;\n> +       double referenceLux_;\n> +};\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> index a7f16ff63079..f2b2f4be50db 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -11,6 +11,7 @@ libipa_headers = files([\n>      'histogram.h',\n>      'interpolator.h',\n>      'lsc_polynomial.h',\n> +    'lux.h',\n>      'module.h',\n>      'pwl.h',\n>      'vector.h',\n> @@ -27,6 +28,7 @@ libipa_sources = files([\n>      'histogram.cpp',\n>      'interpolator.cpp',\n>      'lsc_polynomial.cpp',\n> +    'lux.cpp',\n>      'module.cpp',\n>      'pwl.cpp',\n>      'vector.cpp',\n> -- \n> 2.39.2\n>","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 118E1C32A3\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 20 Dec 2024 10:29:02 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id BE23668495;\n\tFri, 20 Dec 2024 11:29:00 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 494516848B\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Dec 2024 11:28:59 +0100 (CET)","from pendragon.ideasonboard.com\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id DD1E57E2;\n\tFri, 20 Dec 2024 11:28:19 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"s353PB9d\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1734690500;\n\tbh=IQuWMrUFXUxlbVEAN6WwuSGEvDFFxb+yVCtpXSnR+Aw=;\n\th=In-Reply-To:References:Subject:From:Cc:To:Date:From;\n\tb=s353PB9d0GfA/GS/J9Rfb31o/XnxKc87BvbcA8qCeNwg8yIoSW6VEWbARh4hV2WeY\n\thvx+MxFHR6+toGVI2fqvxXTxUgi7j/RwBzG960ukzKSL25PoWt6v5g1o4YsItsjFOh\n\t4bC/ENTuYrkTYbyaQ/n5iEgScACE79jCeVO5GKME=","Content-Type":"text/plain; charset=\"utf-8\"","MIME-Version":"1.0","Content-Transfer-Encoding":"quoted-printable","In-Reply-To":"<20241218074601.3552093-2-paul.elder@ideasonboard.com>","References":"<20241218074601.3552093-1-paul.elder@ideasonboard.com>\n\t<20241218074601.3552093-2-paul.elder@ideasonboard.com>","Subject":"Re: [PATCH v3 1/2] ipa: libipa: Add Lux helper","From":"Kieran Bingham <kieran.bingham@ideasonboard.com>","Cc":"Paul Elder <paul.elder@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>,\n\tlaurent.pinchart@ideasonboard.com, \n\tlibcamera-devel@lists.libcamera.org, stefan.klug@ideasonboard.com","Date":"Fri, 20 Dec 2024 10:28:55 +0000","Message-ID":"<173469053598.547566.6604736916896487019@ping.linuxembedded.co.uk>","User-Agent":"alot/0.10","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>"}},{"id":32902,"web_url":"https://patchwork.libcamera.org/comment/32902/","msgid":"<f27aee9269ec41c49468d26cfd01bab9afd3f372.camel@ideasonboard.com>","date":"2024-12-20T16:11:50","subject":"Re: [PATCH v3 1/2] ipa: libipa: Add Lux helper","submitter":{"id":215,"url":"https://patchwork.libcamera.org/api/people/215/","name":"Isaac Scott","email":"isaac.scott@ideasonboard.com"},"content":"On Wed, 2024-12-18 at 16:46 +0900, Paul Elder wrote:\n> Add a Lux helper to libipa that does the estimation of the lux level\n> given gain, exposure, and luminance histogram. The helper also\n> handles reading the reference values from the tuning file. These are\n> expected to be common operations of lux algorithm modules in IPAs,\n> and\n> is modeled/copied from Raspberry Pi.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> \n> ---\n> Changes in v3:\n> - improve documentation\n> - minor docs formatting fixes\n> - replace setBinSize() with constructor parameter\n> - s/readYaml/parseTuningData/\n> - remove unnecessary includes\n> \n> Changes in v2:\n> - improve documentation\n> - add binSize member variable and corresponding setter\n> - remove aperture\n> - split gain into analogue and digital\n> - change tuning file names into camel case\n> ---\n>  src/ipa/libipa/lux.cpp     | 181\n> +++++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/lux.h       |  42 +++++++++\n>  src/ipa/libipa/meson.build |   2 +\n>  3 files changed, 225 insertions(+)\n>  create mode 100644 src/ipa/libipa/lux.cpp\n>  create mode 100644 src/ipa/libipa/lux.h\n> \n> diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp\n> new file mode 100644\n> index 000000000000..b60060e21803\n> --- /dev/null\n> +++ b/src/ipa/libipa/lux.cpp\n> @@ -0,0 +1,181 @@\n> +/* SPDX-License-Identifier: BSD-2-Clause */\n> +/*\n> + * Copyright (C) 2019, Raspberry Pi Ltd\n> + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>\n> + *\n> + * Helper class that implements lux estimation\n> + */\n> +#include \"lux.h\"\n> +\n> +#include <algorithm>\n> +#include <chrono>\n> +\n> +#include <libcamera/base/log.h>\n> +\n> +#include \"libcamera/internal/yaml_parser.h\"\n> +\n> +#include \"histogram.h\"\n> +\n> +/**\n> + * \\file lux.h\n> + * \\brief Helper class that implements lux estimation\n> + *\n> + * Estimating the lux level of an image is a common operation that\n> can for\n> + * instance be used to adjust the target Y value in AGC or for\n> Bayesian AWB\n> + * estimation.\n> + */\n> +\n> +namespace libcamera {\n> +\n> +using namespace std::literals::chrono_literals;\n> +\n> +LOG_DEFINE_CATEGORY(Lux)\n> +\n> +namespace ipa {\n> +\n> +/**\n> + * \\class Lux\n> + * \\brief Class that implements lux estimation\n> + *\n> + * IPAs that wish to use lux esimation should create a Lux algorithm\n> module\n\ns/esimation/estimation\n\nVery nitpicky... but otherwise\nReviewed-by: Isaac Scott <isaac.scott@ideasonboard.com>\n\n\n> + * that lightly wraps this module by providing the platform-specific\n> luminance\n> + * histogram. The Lux entry in the tuning file must then precede the\n> algorithms\n> + * that depend on the estimated lux value.\n> + */\n> +\n> +/**\n> + * \\var Lux::binSize_\n> + * \\brief The maximum count of each bin\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceExposureTime_\n> + * \\brief The exposure time of the reference image, in microseconds\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceAnalogueGain_\n> + * \\brief The analogue gain of the reference image\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceDigitalGain_\n> + * \\brief The analogue gain of the reference image\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceY_\n> + * \\brief The measured luminance of the reference image, out of the\n> bin size\n> + *\n> + * \\sa binSize_\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceLux_\n> + * \\brief The estimated lux level of the reference image\n> + */\n> +\n> +/**\n> +  * \\brief Construct the Lux helper module\n> +  * \\param[in] binSize The maximum count of each bin\n> +  */\n> +Lux::Lux(unsigned int binSize)\n> +\t: binSize_(binSize)\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Parse tuning data\n> + * \\param[in] tuningData The YamlObject representing the tuning data\n> + *\n> + * This function parses yaml tuning data for the common Lux module.\n> It requires\n> + * reference exposure time, analogue gain, digital gain, and lux\n> values.\n> + *\n> + * \\code{.unparsed}\n> + * algorithms:\n> + *   - Lux:\n> + *       referenceExposureTime: 10000\n> + *       referenceAnalogueGain: 4.0\n> + *       referenceDigitalGain: 1.0\n> + *       referenceY: 12000\n> + *       referenceLux: 1000\n> + * \\endcode\n> + *\n> + * \\return 0 on success or a negative error code\n> + */\n> +int Lux::parseTuningData(const YamlObject &tuningData)\n> +{\n> +\tauto value =\n> tuningData[\"referenceExposureTime\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceExposureTime'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceExposureTime_ = *value * 1.0us;\n> +\n> +\tvalue = tuningData[\"referenceAnalogueGain\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceAnalogueGain'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceAnalogueGain_ = *value;\n> +\n> +\tvalue = tuningData[\"referenceDigitalGain\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceDigitalGain'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceDigitalGain_ = *value;\n> +\n> +\tvalue = tuningData[\"referenceY\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceY'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceY_ = *value;\n> +\n> +\tvalue = tuningData[\"referenceLux\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceLux'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceLux_ = *value;\n> +\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\brief Estimate lux given runtime values\n> + * \\param[in] exposureTime Exposure time applied to the frame\n> + * \\param[in] aGain Analogue gain applied to the frame\n> + * \\param[in] dGain Digital gain applied to the frame\n> + * \\param[in] yHist Histogram from the ISP statistics\n> + *\n> + * Estimate the lux given the exposure time, gain, and histogram.\n> + *\n> + * \\return Estimated lux value\n> + */\n> +double Lux::estimateLux(utils::Duration exposureTime,\n> +\t\t\tdouble aGain, double dGain,\n> +\t\t\tconst Histogram &yHist) const\n> +{\n> +\tdouble currentY = yHist.interQuantileMean(0, 1);\n> +\tdouble exposureTimeRatio = referenceExposureTime_ /\n> exposureTime;\n> +\tdouble aGainRatio = referenceAnalogueGain_ / aGain;\n> +\tdouble dGainRatio = referenceDigitalGain_ / dGain;\n> +\tdouble yRatio = currentY * (binSize_ / yHist.bins()) /\n> referenceY_;\n> +\n> +\tdouble estimatedLux = exposureTimeRatio * aGainRatio *\n> dGainRatio *\n> +\t\t\t      yRatio * referenceLux_;\n> +\n> +\tLOG(Lux, Debug) << \"Estimated lux \" << estimatedLux;\n> +\treturn estimatedLux;\n> +}\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/lux.h b/src/ipa/libipa/lux.h\n> new file mode 100644\n> index 000000000000..93ca64795803\n> --- /dev/null\n> +++ b/src/ipa/libipa/lux.h\n> @@ -0,0 +1,42 @@\n> +/* SPDX-License-Identifier: BSD-2-Clause */\n> +/*\n> + * Copyright (C) 2019, Raspberry Pi Ltd\n> + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>\n> + *\n> + * Helper class that implements lux estimation\n> + */\n> +\n> +#pragma once\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +namespace libcamera {\n> +\n> +class YamlObject;\n> +\n> +namespace ipa {\n> +\n> +class Histogram;\n> +\n> +class Lux\n> +{\n> +public:\n> +\tLux(unsigned int binSize);\n> +\n> +\tint parseTuningData(const YamlObject &tuningData);\n> +\tdouble estimateLux(utils::Duration exposureTime,\n> +\t\t\t   double aGain, double dGain,\n> +\t\t\t   const Histogram &yHist) const;\n> +\n> +private:\n> +\tunsigned int binSize_;\n> +\tutils::Duration referenceExposureTime_;\n> +\tdouble referenceAnalogueGain_;\n> +\tdouble referenceDigitalGain_;\n> +\tdouble referenceY_;\n> +\tdouble referenceLux_;\n> +};\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> index a7f16ff63079..f2b2f4be50db 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -11,6 +11,7 @@ libipa_headers = files([\n>      'histogram.h',\n>      'interpolator.h',\n>      'lsc_polynomial.h',\n> +    'lux.h',\n>      'module.h',\n>      'pwl.h',\n>      'vector.h',\n> @@ -27,6 +28,7 @@ libipa_sources = files([\n>      'histogram.cpp',\n>      'interpolator.cpp',\n>      'lsc_polynomial.cpp',\n> +    'lux.cpp',\n>      'module.cpp',\n>      'pwl.cpp',\n>      'vector.cpp',","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 01FC8C327D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tFri, 20 Dec 2024 16:11:56 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EF5FC684A2;\n\tFri, 20 Dec 2024 17:11:55 +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 80B7C6808F\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tFri, 20 Dec 2024 17:11:53 +0100 (CET)","from isaac-ThinkPad-T16-Gen-2.lan\n\t(cpc89244-aztw30-2-0-cust6594.18-1.cable.virginm.net [86.31.185.195])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id B0D8CB2B;\n\tFri, 20 Dec 2024 17:11:13 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"lDBoMqKP\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1734711073;\n\tbh=9rOoPfO0b6w8gd63MXhdwSafpyouF5nHjkfFDEvlhaQ=;\n\th=Subject:From:To:Date:In-Reply-To:References:From;\n\tb=lDBoMqKPbw70d+vHwwI7iJ/FWzOQ564SNUBnugr39Ir3nNEbWNjxZMSmhnH6pwQ0d\n\tzzfX6MIIa439lcpM7U5ecE4TsbimYhmkqElLB9WmpFgzJuh1TmO42Y2TejCx8pS1wk\n\tj02Q6jGEjpjpgrkwADjJxGRdc1JXqKQh4J01J/SU=","Message-ID":"<f27aee9269ec41c49468d26cfd01bab9afd3f372.camel@ideasonboard.com>","Subject":"Re: [PATCH v3 1/2] ipa: libipa: Add Lux helper","From":"Isaac Scott <isaac.scott@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>, \n\tlibcamera-devel@lists.libcamera.org, laurent.pinchart@ideasonboard.com,\n\tstefan.klug@ideasonboard.com","Date":"Fri, 20 Dec 2024 16:11:50 +0000","In-Reply-To":"<20241218074601.3552093-2-paul.elder@ideasonboard.com>","References":"<20241218074601.3552093-1-paul.elder@ideasonboard.com>\n\t<20241218074601.3552093-2-paul.elder@ideasonboard.com>","Content-Type":"text/plain; charset=\"UTF-8\"","Content-Transfer-Encoding":"quoted-printable","User-Agent":"Evolution 3.54.2 (by Flathub.org) ","MIME-Version":"1.0","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>"}},{"id":33094,"web_url":"https://patchwork.libcamera.org/comment/33094/","msgid":"<20250119235225.GA29556@pendragon.ideasonboard.com>","date":"2025-01-19T23:52:25","subject":"Re: [PATCH v3 1/2] ipa: libipa: Add Lux helper","submitter":{"id":2,"url":"https://patchwork.libcamera.org/api/people/2/","name":"Laurent Pinchart","email":"laurent.pinchart@ideasonboard.com"},"content":"One additional small comment.\n\nOn Wed, Dec 18, 2024 at 04:46:00PM +0900, Paul Elder wrote:\n> Add a Lux helper to libipa that does the estimation of the lux level\n> given gain, exposure, and luminance histogram. The helper also\n> handles reading the reference values from the tuning file. These are\n> expected to be common operations of lux algorithm modules in IPAs, and\n> is modeled/copied from Raspberry Pi.\n> \n> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>\n> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>\n> \n> ---\n> Changes in v3:\n> - improve documentation\n> - minor docs formatting fixes\n> - replace setBinSize() with constructor parameter\n> - s/readYaml/parseTuningData/\n> - remove unnecessary includes\n> \n> Changes in v2:\n> - improve documentation\n> - add binSize member variable and corresponding setter\n> - remove aperture\n> - split gain into analogue and digital\n> - change tuning file names into camel case\n> ---\n>  src/ipa/libipa/lux.cpp     | 181 +++++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/lux.h       |  42 +++++++++\n>  src/ipa/libipa/meson.build |   2 +\n>  3 files changed, 225 insertions(+)\n>  create mode 100644 src/ipa/libipa/lux.cpp\n>  create mode 100644 src/ipa/libipa/lux.h\n> \n> diff --git a/src/ipa/libipa/lux.cpp b/src/ipa/libipa/lux.cpp\n> new file mode 100644\n> index 000000000000..b60060e21803\n> --- /dev/null\n> +++ b/src/ipa/libipa/lux.cpp\n> @@ -0,0 +1,181 @@\n> +/* SPDX-License-Identifier: BSD-2-Clause */\n> +/*\n> + * Copyright (C) 2019, Raspberry Pi Ltd\n> + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>\n> + *\n> + * Helper class that implements lux estimation\n> + */\n> +#include \"lux.h\"\n> +\n> +#include <algorithm>\n> +#include <chrono>\n> +\n> +#include <libcamera/base/log.h>\n> +\n> +#include \"libcamera/internal/yaml_parser.h\"\n> +\n> +#include \"histogram.h\"\n> +\n> +/**\n> + * \\file lux.h\n> + * \\brief Helper class that implements lux estimation\n> + *\n> + * Estimating the lux level of an image is a common operation that can for\n> + * instance be used to adjust the target Y value in AGC or for Bayesian AWB\n> + * estimation.\n> + */\n> +\n> +namespace libcamera {\n> +\n> +using namespace std::literals::chrono_literals;\n> +\n> +LOG_DEFINE_CATEGORY(Lux)\n> +\n> +namespace ipa {\n> +\n> +/**\n> + * \\class Lux\n> + * \\brief Class that implements lux estimation\n> + *\n> + * IPAs that wish to use lux esimation should create a Lux algorithm module\n> + * that lightly wraps this module by providing the platform-specific luminance\n> + * histogram. The Lux entry in the tuning file must then precede the algorithms\n> + * that depend on the estimated lux value.\n> + */\n> +\n> +/**\n> + * \\var Lux::binSize_\n> + * \\brief The maximum count of each bin\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceExposureTime_\n> + * \\brief The exposure time of the reference image, in microseconds\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceAnalogueGain_\n> + * \\brief The analogue gain of the reference image\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceDigitalGain_\n> + * \\brief The analogue gain of the reference image\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceY_\n> + * \\brief The measured luminance of the reference image, out of the bin size\n> + *\n> + * \\sa binSize_\n> + */\n> +\n> +/**\n> + * \\var Lux::referenceLux_\n> + * \\brief The estimated lux level of the reference image\n> + */\n> +\n> +/**\n> +  * \\brief Construct the Lux helper module\n> +  * \\param[in] binSize The maximum count of each bin\n> +  */\n\nWrong indentation.\n\n> +Lux::Lux(unsigned int binSize)\n> +\t: binSize_(binSize)\n> +{\n> +}\n> +\n> +/**\n> + * \\brief Parse tuning data\n> + * \\param[in] tuningData The YamlObject representing the tuning data\n> + *\n> + * This function parses yaml tuning data for the common Lux module. It requires\n> + * reference exposure time, analogue gain, digital gain, and lux values.\n> + *\n> + * \\code{.unparsed}\n> + * algorithms:\n> + *   - Lux:\n> + *       referenceExposureTime: 10000\n> + *       referenceAnalogueGain: 4.0\n> + *       referenceDigitalGain: 1.0\n> + *       referenceY: 12000\n> + *       referenceLux: 1000\n> + * \\endcode\n> + *\n> + * \\return 0 on success or a negative error code\n> + */\n> +int Lux::parseTuningData(const YamlObject &tuningData)\n> +{\n> +\tauto value = tuningData[\"referenceExposureTime\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceExposureTime'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceExposureTime_ = *value * 1.0us;\n> +\n> +\tvalue = tuningData[\"referenceAnalogueGain\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceAnalogueGain'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceAnalogueGain_ = *value;\n> +\n> +\tvalue = tuningData[\"referenceDigitalGain\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceDigitalGain'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceDigitalGain_ = *value;\n> +\n> +\tvalue = tuningData[\"referenceY\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceY'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceY_ = *value;\n> +\n> +\tvalue = tuningData[\"referenceLux\"].get<double>();\n> +\tif (!value) {\n> +\t\tLOG(Lux, Error) << \"Missing tuning parameter: \"\n> +\t\t\t\t<< \"'referenceLux'\";\n> +\t\treturn -EINVAL;\n> +\t}\n> +\treferenceLux_ = *value;\n> +\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\brief Estimate lux given runtime values\n> + * \\param[in] exposureTime Exposure time applied to the frame\n> + * \\param[in] aGain Analogue gain applied to the frame\n> + * \\param[in] dGain Digital gain applied to the frame\n> + * \\param[in] yHist Histogram from the ISP statistics\n> + *\n> + * Estimate the lux given the exposure time, gain, and histogram.\n> + *\n> + * \\return Estimated lux value\n> + */\n> +double Lux::estimateLux(utils::Duration exposureTime,\n> +\t\t\tdouble aGain, double dGain,\n> +\t\t\tconst Histogram &yHist) const\n> +{\n> +\tdouble currentY = yHist.interQuantileMean(0, 1);\n> +\tdouble exposureTimeRatio = referenceExposureTime_ / exposureTime;\n> +\tdouble aGainRatio = referenceAnalogueGain_ / aGain;\n> +\tdouble dGainRatio = referenceDigitalGain_ / dGain;\n> +\tdouble yRatio = currentY * (binSize_ / yHist.bins()) / referenceY_;\n> +\n> +\tdouble estimatedLux = exposureTimeRatio * aGainRatio * dGainRatio *\n> +\t\t\t      yRatio * referenceLux_;\n> +\n> +\tLOG(Lux, Debug) << \"Estimated lux \" << estimatedLux;\n> +\treturn estimatedLux;\n> +}\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/lux.h b/src/ipa/libipa/lux.h\n> new file mode 100644\n> index 000000000000..93ca64795803\n> --- /dev/null\n> +++ b/src/ipa/libipa/lux.h\n> @@ -0,0 +1,42 @@\n> +/* SPDX-License-Identifier: BSD-2-Clause */\n> +/*\n> + * Copyright (C) 2019, Raspberry Pi Ltd\n> + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com>\n> + *\n> + * Helper class that implements lux estimation\n> + */\n> +\n> +#pragma once\n> +\n> +#include <libcamera/base/utils.h>\n> +\n> +namespace libcamera {\n> +\n> +class YamlObject;\n> +\n> +namespace ipa {\n> +\n> +class Histogram;\n> +\n> +class Lux\n> +{\n> +public:\n> +\tLux(unsigned int binSize);\n> +\n> +\tint parseTuningData(const YamlObject &tuningData);\n> +\tdouble estimateLux(utils::Duration exposureTime,\n> +\t\t\t   double aGain, double dGain,\n> +\t\t\t   const Histogram &yHist) const;\n> +\n> +private:\n> +\tunsigned int binSize_;\n> +\tutils::Duration referenceExposureTime_;\n> +\tdouble referenceAnalogueGain_;\n> +\tdouble referenceDigitalGain_;\n> +\tdouble referenceY_;\n> +\tdouble referenceLux_;\n> +};\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build\n> index a7f16ff63079..f2b2f4be50db 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -11,6 +11,7 @@ libipa_headers = files([\n>      'histogram.h',\n>      'interpolator.h',\n>      'lsc_polynomial.h',\n> +    'lux.h',\n>      'module.h',\n>      'pwl.h',\n>      'vector.h',\n> @@ -27,6 +28,7 @@ libipa_sources = files([\n>      'histogram.cpp',\n>      'interpolator.cpp',\n>      'lsc_polynomial.cpp',\n> +    'lux.cpp',\n>      'module.cpp',\n>      'pwl.cpp',\n>      'vector.cpp',\n> -- \n> 2.39.2\n>","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 8F31CC327D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSun, 19 Jan 2025 23:52:37 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id AC7A86854D;\n\tMon, 20 Jan 2025 00:52:35 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id D462F68519\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Jan 2025 00:52:32 +0100 (CET)","from pendragon.ideasonboard.com (81-175-209-231.bb.dnainternet.fi\n\t[81.175.209.231])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 3A20066B;\n\tMon, 20 Jan 2025 00:51:31 +0100 (CET)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=ideasonboard.com header.i=@ideasonboard.com\n\theader.b=\"WGzfC+Sl\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1737330691;\n\tbh=FD88GuPonl5E7SpaR/kDkyaeIKbJb9CArMXLfv+deds=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=WGzfC+SlIXiujkOeLZKWvGKgEPp3b9M4te1y2jlJb6xiOwKSDv6hH0vCi/zSVmkta\n\tuDk/uekqo3RYgxhWqScX2iaprV4dVH1WV3juJ6OzjjlvKCPCZ9SgPqeKfPFr0A9MPr\n\t8Rtbjv6PvzwFUNEjVeNQ7i9YtWXbPY7HyQMsOLJI=","Date":"Mon, 20 Jan 2025 01:52:25 +0200","From":"Laurent Pinchart <laurent.pinchart@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org, stefan.klug@ideasonboard.com","Subject":"Re: [PATCH v3 1/2] ipa: libipa: Add Lux helper","Message-ID":"<20250119235225.GA29556@pendragon.ideasonboard.com>","References":"<20241218074601.3552093-1-paul.elder@ideasonboard.com>\n\t<20241218074601.3552093-2-paul.elder@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<20241218074601.3552093-2-paul.elder@ideasonboard.com>","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>"}}]