[{"id":33062,"web_url":"https://patchwork.libcamera.org/comment/33062/","msgid":"<Z4WYZNzid_vu6VrY@pyrite.rasen.tech>","date":"2025-01-13T22:49:08","subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","submitter":{"id":17,"url":"https://patchwork.libcamera.org/api/people/17/","name":"Paul Elder","email":"paul.elder@ideasonboard.com"},"content":"On Thu, Jan 09, 2025 at 12:53:56PM +0100, Stefan Klug wrote:\n> Add the grey world algorithm that is currently used in rkisp1 to libipa.\n> No changes in functionality were made.\n> \n> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> ---\n>  src/ipa/libipa/awb_grey.cpp | 114 ++++++++++++++++++++++++++++++++++++\n>  src/ipa/libipa/awb_grey.h   |  35 +++++++++++\n>  src/ipa/libipa/meson.build  |   2 +\n>  3 files changed, 151 insertions(+)\n>  create mode 100644 src/ipa/libipa/awb_grey.cpp\n>  create mode 100644 src/ipa/libipa/awb_grey.h\n> \n> diff --git a/src/ipa/libipa/awb_grey.cpp b/src/ipa/libipa/awb_grey.cpp\n> new file mode 100644\n> index 000000000000..192a7cf3834a\n> --- /dev/null\n> +++ b/src/ipa/libipa/awb_grey.cpp\n> @@ -0,0 +1,114 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024 Ideas on Board Oy\n> + *\n> + * Base class for bayesian AWB algorithm\n> + */\n> +\n> +#include \"awb_grey.h\"\n> +\n> +#include <cmath>\n> +\n> +#include <libcamera/base/log.h>\n> +#include <libcamera/control_ids.h>\n> +\n> +#include \"colours.h\"\n> +\n> +using namespace libcamera::controls;\n> +\n> +/**\n> + * \\file awb_grey.h\n> + * \\brief Implementation of a grey world AWB algorithm\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(Awb)\n> +namespace ipa {\n> +\n> +/**\n> + * \\class AwbGrey\n> + * \\brief A Grey world auto white balance algorithm\n> + */\n> +\n> +/**\n> + * \\brief Initialize the algorithm with the given tuning data\n> + * \\param[in] tuningData The tuning data for the algorithm\n> + *\n> + * Load the colour temperature curve from the tuning data. If there is no tuning\n> + * data available, continue with a warning. Manual colour temperature will not\n> + * work in that case.\n> + *\n> + * \\return 0 on success, a negative error code otherwise\n> + */\n> +int AwbGrey::init(const YamlObject &tuningData)\n> +{\n> +\tInterpolator<Vector<double, 2>> gains;\n> +\tint ret = gains.readYaml(tuningData[\"colourGains\"], \"ct\", \"gains\");\n> +\tif (ret < 0)\n> +\t\tLOG(Awb, Warning)\n> +\t\t\t<< \"Failed to parse 'colourGains' \"\n> +\t\t\t<< \"parameter from tuning file; \"\n> +\t\t\t<< \"manual colour temperature will not work properly\";\n> +\telse\n> +\t\tcolourGainCurve_ = gains;\n> +\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\brief Calculate awb data from the given statistics\n> + * \\param[in] stats The statistics to use for the calculation\n> + * \\param[in] lux The lux value of the scene\n> + *\n> + * Estimates the colour temperature based on the coulours::estimateCCT function.\n\ns/coulours/colors/ -- wait no -- s/coulours/colours/\n\n> + * The gains are calculated purely based on the RGB means provided by the \\a\n> + * stats. The colour temperature is not taken into account when calculating the\n> + * gains.\n> + *\n> + * The \\a lux parameter is not used in this algorithm.\n> + *\n> + * \\return The awb result\n> + */\n> +AwbResult AwbGrey::calculateAwb(const AwbStats &stats, [[maybe_unused]] int lux)\n> +{\n> +\tAwbResult result;\n> +\tauto means = stats.getRGBMeans();\n> +\tresult.colourTemperature = estimateCCT(means);\n> +\n> +\t/*\n> +\t * Estimate the red and blue gains to apply in a grey world. The green\n> +\t * gain is hardcoded to 1.0. Avoid divisions by zero by clamping the\n> +\t * divisor to a minimum value of 1.0.\n> +\t */\n> +\tresult.gains.r() = means.g() / std::max(means.r(), 1.0);\n> +\tresult.gains.g() = 1.0;\n> +\tresult.gains.b() = means.g() / std::max(means.b(), 1.0);\n> +\treturn result;\n> +}\n> +\n> +/**\n> + * \\brief Compute white balance gains from a colour temperature\n> + * \\param[in] colourTemperature The colour temperature in Kelvin\n> + *\n> + * Compute the white balance gains from a \\a colourTemperature. This function\n> + * does not take any statistics into account. It simply interpolates the colour\n> + * gains configured in the colour temperature curve.\n> + *\n> + * \\return The colour gains if a colour temperature curve is available,\n> + * [1, 1, 1] otherwise.\n> + */\n> +RGB<double> AwbGrey::gainsFromColourTemperature(double colourTemperature)\n> +{\n> +\tif (!colourGainCurve_) {\n> +\t\tLOG(Awb, Error) << \"No gains defined\";\n> +\t\treturn RGB<double>({ 1.0, 1.0, 1.0 });\n> +\t}\n> +\n> +\tauto gains = colourGainCurve_->getInterpolated(colourTemperature);\n> +\treturn { { gains[0], 1.0, gains[0] } };\n\nShould the second gains[0] be gains[1]...?\n\nOtherwise looks good to me.\n\n\nPaul\n\n\n> +}\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/awb_grey.h b/src/ipa/libipa/awb_grey.h\n> new file mode 100644\n> index 000000000000..6eda8e5498fb\n> --- /dev/null\n> +++ b/src/ipa/libipa/awb_grey.h\n> @@ -0,0 +1,35 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024 Ideas on Board Oy\n> + *\n> + * AWB grey world algorithm\n> + */\n> +\n> +#pragma once\n> +\n> +#include \"libcamera/internal/yaml_parser.h\"\n> +\n> +#include \"awb.h\"\n> +#include \"interpolator.h\"\n> +#include \"vector.h\"\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +class AwbGrey : public AwbAlgorithm\n> +{\n> +public:\n> +\tAwbGrey() = default;\n> +\n> +\tint init(const YamlObject &tuningData) override;\n> +\tAwbResult calculateAwb(const AwbStats &stats, int lux) override;\n> +\tRGB<double> gainsFromColourTemperature(double coulourTemperature) override;\n> +\n> +private:\n> +\tstd::optional<Interpolator<Vector<double, 2>>> colourGainCurve_;\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 03e879c5834f..c550a6eb45b6 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -3,6 +3,7 @@\n>  libipa_headers = files([\n>      'agc_mean_luminance.h',\n>      'algorithm.h',\n> +    'awb_grey.h',\n>      'awb.h',\n>      'camera_sensor_helper.h',\n>      'colours.h',\n> @@ -21,6 +22,7 @@ libipa_headers = files([\n>  libipa_sources = files([\n>      'agc_mean_luminance.cpp',\n>      'algorithm.cpp',\n> +    'awb_grey.cpp',\n>      'awb.cpp',\n>      'camera_sensor_helper.cpp',\n>      'colours.cpp',\n> -- \n> 2.43.0\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 966EAC326C\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 13 Jan 2025 22:49:48 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C73FF684E7;\n\tMon, 13 Jan 2025 23:49:47 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 626E3684E7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 13 Jan 2025 23:49:45 +0100 (CET)","from pyrite.rasen.tech (unknown [173.16.167.215])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 96ADA74A;\n\tMon, 13 Jan 2025 23:48:47 +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=\"HZvOv0r1\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1736808528;\n\tbh=/l7DopJJVKFDJHg2b8uHvIiM4/G4krX/M0XJKiegi18=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=HZvOv0r1rNjI0gT5oZrAQVcCkjUA/L5kJHImkCYZUj9+ZwjOrpaQfSUukB+1+grxj\n\tePD7XDL3Dd9HxkzEWiCJ2ud1R4avsCmO1b0UWauhPVD86rb/bsJX73I3gGwBJoAiqK\n\t5p5mS/f6jLYJ/8xm1HErNb7CtRU+nmKO55GjM10U=","Date":"Mon, 13 Jan 2025 16:49:08 -0600","From":"Paul Elder <paul.elder@ideasonboard.com>","To":"Stefan Klug <stefan.klug@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","Message-ID":"<Z4WYZNzid_vu6VrY@pyrite.rasen.tech>","References":"<20250109115412.356768-1-stefan.klug@ideasonboard.com>\n\t<20250109115412.356768-6-stefan.klug@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=us-ascii","Content-Disposition":"inline","In-Reply-To":"<20250109115412.356768-6-stefan.klug@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>"}},{"id":33086,"web_url":"https://patchwork.libcamera.org/comment/33086/","msgid":"<ynfjmyrlkemvdehkysg2qya2rs7oaqbpbjis2c6gc5crj5nonw@imudmduyycfb>","date":"2025-01-16T19:38:59","subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","submitter":{"id":184,"url":"https://patchwork.libcamera.org/api/people/184/","name":"Stefan Klug","email":"stefan.klug@ideasonboard.com"},"content":"Hi Paul,\n\nThank you for the review. \n\nOn Mon, Jan 13, 2025 at 04:49:08PM -0600, Paul Elder wrote:\n> On Thu, Jan 09, 2025 at 12:53:56PM +0100, Stefan Klug wrote:\n> > Add the grey world algorithm that is currently used in rkisp1 to libipa.\n> > No changes in functionality were made.\n> > \n> > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> > ---\n> >  src/ipa/libipa/awb_grey.cpp | 114 ++++++++++++++++++++++++++++++++++++\n> >  src/ipa/libipa/awb_grey.h   |  35 +++++++++++\n> >  src/ipa/libipa/meson.build  |   2 +\n> >  3 files changed, 151 insertions(+)\n> >  create mode 100644 src/ipa/libipa/awb_grey.cpp\n> >  create mode 100644 src/ipa/libipa/awb_grey.h\n> > \n> > diff --git a/src/ipa/libipa/awb_grey.cpp b/src/ipa/libipa/awb_grey.cpp\n> > new file mode 100644\n> > index 000000000000..192a7cf3834a\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/awb_grey.cpp\n> > @@ -0,0 +1,114 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2024 Ideas on Board Oy\n> > + *\n> > + * Base class for bayesian AWB algorithm\n> > + */\n> > +\n> > +#include \"awb_grey.h\"\n> > +\n> > +#include <cmath>\n> > +\n> > +#include <libcamera/base/log.h>\n> > +#include <libcamera/control_ids.h>\n> > +\n> > +#include \"colours.h\"\n> > +\n> > +using namespace libcamera::controls;\n> > +\n> > +/**\n> > + * \\file awb_grey.h\n> > + * \\brief Implementation of a grey world AWB algorithm\n> > + */\n> > +\n> > +namespace libcamera {\n> > +\n> > +LOG_DECLARE_CATEGORY(Awb)\n> > +namespace ipa {\n> > +\n> > +/**\n> > + * \\class AwbGrey\n> > + * \\brief A Grey world auto white balance algorithm\n> > + */\n> > +\n> > +/**\n> > + * \\brief Initialize the algorithm with the given tuning data\n> > + * \\param[in] tuningData The tuning data for the algorithm\n> > + *\n> > + * Load the colour temperature curve from the tuning data. If there is no tuning\n> > + * data available, continue with a warning. Manual colour temperature will not\n> > + * work in that case.\n> > + *\n> > + * \\return 0 on success, a negative error code otherwise\n> > + */\n> > +int AwbGrey::init(const YamlObject &tuningData)\n> > +{\n> > +\tInterpolator<Vector<double, 2>> gains;\n> > +\tint ret = gains.readYaml(tuningData[\"colourGains\"], \"ct\", \"gains\");\n> > +\tif (ret < 0)\n> > +\t\tLOG(Awb, Warning)\n> > +\t\t\t<< \"Failed to parse 'colourGains' \"\n> > +\t\t\t<< \"parameter from tuning file; \"\n> > +\t\t\t<< \"manual colour temperature will not work properly\";\n> > +\telse\n> > +\t\tcolourGainCurve_ = gains;\n> > +\n> > +\treturn 0;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Calculate awb data from the given statistics\n> > + * \\param[in] stats The statistics to use for the calculation\n> > + * \\param[in] lux The lux value of the scene\n> > + *\n> > + * Estimates the colour temperature based on the coulours::estimateCCT function.\n> \n> s/coulours/colors/ -- wait no -- s/coulours/colours/\n> \n> > + * The gains are calculated purely based on the RGB means provided by the \\a\n> > + * stats. The colour temperature is not taken into account when calculating the\n> > + * gains.\n> > + *\n> > + * The \\a lux parameter is not used in this algorithm.\n> > + *\n> > + * \\return The awb result\n> > + */\n> > +AwbResult AwbGrey::calculateAwb(const AwbStats &stats, [[maybe_unused]] int lux)\n> > +{\n> > +\tAwbResult result;\n> > +\tauto means = stats.getRGBMeans();\n> > +\tresult.colourTemperature = estimateCCT(means);\n> > +\n> > +\t/*\n> > +\t * Estimate the red and blue gains to apply in a grey world. The green\n> > +\t * gain is hardcoded to 1.0. Avoid divisions by zero by clamping the\n> > +\t * divisor to a minimum value of 1.0.\n> > +\t */\n> > +\tresult.gains.r() = means.g() / std::max(means.r(), 1.0);\n> > +\tresult.gains.g() = 1.0;\n> > +\tresult.gains.b() = means.g() / std::max(means.b(), 1.0);\n> > +\treturn result;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Compute white balance gains from a colour temperature\n> > + * \\param[in] colourTemperature The colour temperature in Kelvin\n> > + *\n> > + * Compute the white balance gains from a \\a colourTemperature. This function\n> > + * does not take any statistics into account. It simply interpolates the colour\n> > + * gains configured in the colour temperature curve.\n> > + *\n> > + * \\return The colour gains if a colour temperature curve is available,\n> > + * [1, 1, 1] otherwise.\n> > + */\n> > +RGB<double> AwbGrey::gainsFromColourTemperature(double colourTemperature)\n> > +{\n> > +\tif (!colourGainCurve_) {\n> > +\t\tLOG(Awb, Error) << \"No gains defined\";\n> > +\t\treturn RGB<double>({ 1.0, 1.0, 1.0 });\n> > +\t}\n> > +\n> > +\tauto gains = colourGainCurve_->getInterpolated(colourTemperature);\n> > +\treturn { { gains[0], 1.0, gains[0] } };\n> \n> Should the second gains[0] be gains[1]...?\n\nOuch. How did that slip through...\n\nThanks for spotting.\n\nCheers,\nStefan\n\n> \n> Otherwise looks good to me.\n> \n> \n> Paul\n> \n> \n> > +}\n> > +\n> > +} /* namespace ipa */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/ipa/libipa/awb_grey.h b/src/ipa/libipa/awb_grey.h\n> > new file mode 100644\n> > index 000000000000..6eda8e5498fb\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/awb_grey.h\n> > @@ -0,0 +1,35 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2024 Ideas on Board Oy\n> > + *\n> > + * AWB grey world algorithm\n> > + */\n> > +\n> > +#pragma once\n> > +\n> > +#include \"libcamera/internal/yaml_parser.h\"\n> > +\n> > +#include \"awb.h\"\n> > +#include \"interpolator.h\"\n> > +#include \"vector.h\"\n> > +\n> > +namespace libcamera {\n> > +\n> > +namespace ipa {\n> > +\n> > +class AwbGrey : public AwbAlgorithm\n> > +{\n> > +public:\n> > +\tAwbGrey() = default;\n> > +\n> > +\tint init(const YamlObject &tuningData) override;\n> > +\tAwbResult calculateAwb(const AwbStats &stats, int lux) override;\n> > +\tRGB<double> gainsFromColourTemperature(double coulourTemperature) override;\n> > +\n> > +private:\n> > +\tstd::optional<Interpolator<Vector<double, 2>>> colourGainCurve_;\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 03e879c5834f..c550a6eb45b6 100644\n> > --- a/src/ipa/libipa/meson.build\n> > +++ b/src/ipa/libipa/meson.build\n> > @@ -3,6 +3,7 @@\n> >  libipa_headers = files([\n> >      'agc_mean_luminance.h',\n> >      'algorithm.h',\n> > +    'awb_grey.h',\n> >      'awb.h',\n> >      'camera_sensor_helper.h',\n> >      'colours.h',\n> > @@ -21,6 +22,7 @@ libipa_headers = files([\n> >  libipa_sources = files([\n> >      'agc_mean_luminance.cpp',\n> >      'algorithm.cpp',\n> > +    'awb_grey.cpp',\n> >      'awb.cpp',\n> >      'camera_sensor_helper.cpp',\n> >      'colours.cpp',\n> > -- \n> > 2.43.0\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 25283C3273\n\tfor <parsemail@patchwork.libcamera.org>;\n\tThu, 16 Jan 2025 19:39:06 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C972B68521;\n\tThu, 16 Jan 2025 20:39:04 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 0DE3668506\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tThu, 16 Jan 2025 20:39:03 +0100 (CET)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:4beb:e9d1:94fd:d1ef])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id F20286B5;\n\tThu, 16 Jan 2025 20:38:03 +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=\"e7/KBfRk\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1737056284;\n\tbh=IwFwZYM+zwMXyh53tL8+Ct74dZUpo/b0mxp8PJP2m5E=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=e7/KBfRkIasHlt+Zdgxw+zsKRnT/zfYtCwZzINEjHHaOWNw0bAhVqP7Uaarp4VJAe\n\t2Fs+KH/9I2BeC0qFn7/1jlHvFpcCg9SXyzu8tcdu9jx47YhblmZ8ZZAXJwRXDu3KJK\n\tknUk3iLxomgW3GEk4VAggFTblxeiNx8hpaTCYJDY=","Date":"Thu, 16 Jan 2025 20:38:59 +0100","From":"Stefan Klug <stefan.klug@ideasonboard.com>","To":"Paul Elder <paul.elder@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","Message-ID":"<ynfjmyrlkemvdehkysg2qya2rs7oaqbpbjis2c6gc5crj5nonw@imudmduyycfb>","References":"<20250109115412.356768-1-stefan.klug@ideasonboard.com>\n\t<20250109115412.356768-6-stefan.klug@ideasonboard.com>\n\t<Z4WYZNzid_vu6VrY@pyrite.rasen.tech>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<Z4WYZNzid_vu6VrY@pyrite.rasen.tech>","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":33098,"web_url":"https://patchwork.libcamera.org/comment/33098/","msgid":"<a478dd16-0273-40e4-8d41-228100f43de6@ideasonboard.com>","date":"2025-01-20T10:37:18","subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","submitter":{"id":156,"url":"https://patchwork.libcamera.org/api/people/156/","name":"Dan Scally","email":"dan.scally@ideasonboard.com"},"content":"Hi Stefan\n\nOn 09/01/2025 11:53, Stefan Klug wrote:\n> Add the grey world algorithm that is currently used in rkisp1 to libipa.\n> No changes in functionality were made.\n>\n> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> ---\n>   src/ipa/libipa/awb_grey.cpp | 114 ++++++++++++++++++++++++++++++++++++\n>   src/ipa/libipa/awb_grey.h   |  35 +++++++++++\n>   src/ipa/libipa/meson.build  |   2 +\n>   3 files changed, 151 insertions(+)\n>   create mode 100644 src/ipa/libipa/awb_grey.cpp\n>   create mode 100644 src/ipa/libipa/awb_grey.h\n>\n> diff --git a/src/ipa/libipa/awb_grey.cpp b/src/ipa/libipa/awb_grey.cpp\n> new file mode 100644\n> index 000000000000..192a7cf3834a\n> --- /dev/null\n> +++ b/src/ipa/libipa/awb_grey.cpp\n> @@ -0,0 +1,114 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024 Ideas on Board Oy\n> + *\n> + * Base class for bayesian AWB algorithm\n> + */\n> +\n> +#include \"awb_grey.h\"\n> +\n> +#include <cmath>\n> +\n> +#include <libcamera/base/log.h>\n> +#include <libcamera/control_ids.h>\n> +\n> +#include \"colours.h\"\n> +\n> +using namespace libcamera::controls;\n> +\n> +/**\n> + * \\file awb_grey.h\n> + * \\brief Implementation of a grey world AWB algorithm\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(Awb)\nIs it worth differentiating between this log category and that in awb.cpp - which is also just \n\"Awb\"? The logs will include the file path and line number of course so they can be distinguished \nanyway so probably it doesn't matter, but I thought I'd mention it just to bring it to mind - I'm \nhappy either way.\n> +namespace ipa {\n> +\n> +/**\n> + * \\class AwbGrey\n> + * \\brief A Grey world auto white balance algorithm\n> + */\n> +\n> +/**\n> + * \\brief Initialize the algorithm with the given tuning data\n> + * \\param[in] tuningData The tuning data for the algorithm\n> + *\n> + * Load the colour temperature curve from the tuning data. If there is no tuning\n> + * data available, continue with a warning. Manual colour temperature will not\n> + * work in that case.\n> + *\n> + * \\return 0 on success, a negative error code otherwise\n> + */\n> +int AwbGrey::init(const YamlObject &tuningData)\n> +{\n> +\tInterpolator<Vector<double, 2>> gains;\n> +\tint ret = gains.readYaml(tuningData[\"colourGains\"], \"ct\", \"gains\");\n> +\tif (ret < 0)\n> +\t\tLOG(Awb, Warning)\n> +\t\t\t<< \"Failed to parse 'colourGains' \"\n> +\t\t\t<< \"parameter from tuning file; \"\n> +\t\t\t<< \"manual colour temperature will not work properly\";\n> +\telse\n> +\t\tcolourGainCurve_ = gains;\n> +\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\brief Calculate awb data from the given statistics\n> + * \\param[in] stats The statistics to use for the calculation\n> + * \\param[in] lux The lux value of the scene\n> + *\n> + * Estimates the colour temperature based on the coulours::estimateCCT function.\ns/coulours/colours.\n> + * The gains are calculated purely based on the RGB means provided by the \\a\n> + * stats. The colour temperature is not taken into account when calculating the\n> + * gains.\n> + *\n> + * The \\a lux parameter is not used in this algorithm.\n> + *\n> + * \\return The awb result\n> + */\n> +AwbResult AwbGrey::calculateAwb(const AwbStats &stats, [[maybe_unused]] int lux)\n> +{\n> +\tAwbResult result;\n> +\tauto means = stats.getRGBMeans();\n> +\tresult.colourTemperature = estimateCCT(means);\n> +\n> +\t/*\n> +\t * Estimate the red and blue gains to apply in a grey world. The green\n> +\t * gain is hardcoded to 1.0. Avoid divisions by zero by clamping the\n> +\t * divisor to a minimum value of 1.0.\n> +\t */\n> +\tresult.gains.r() = means.g() / std::max(means.r(), 1.0);\n> +\tresult.gains.g() = 1.0;\n> +\tresult.gains.b() = means.g() / std::max(means.b(), 1.0);\n> +\treturn result;\n> +}\n> +\n> +/**\n> + * \\brief Compute white balance gains from a colour temperature\n> + * \\param[in] colourTemperature The colour temperature in Kelvin\n> + *\n> + * Compute the white balance gains from a \\a colourTemperature. This function\n> + * does not take any statistics into account. It simply interpolates the colour\n> + * gains configured in the colour temperature curve.\n> + *\n> + * \\return The colour gains if a colour temperature curve is available,\n> + * [1, 1, 1] otherwise.\n> + */\n> +RGB<double> AwbGrey::gainsFromColourTemperature(double colourTemperature)\n> +{\n> +\tif (!colourGainCurve_) {\n> +\t\tLOG(Awb, Error) << \"No gains defined\";\n> +\t\treturn RGB<double>({ 1.0, 1.0, 1.0 });\n> +\t}\n> +\n> +\tauto gains = colourGainCurve_->getInterpolated(colourTemperature);\n> +\treturn { { gains[0], 1.0, gains[0] } };\n> +}\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/awb_grey.h b/src/ipa/libipa/awb_grey.h\n> new file mode 100644\n> index 000000000000..6eda8e5498fb\n> --- /dev/null\n> +++ b/src/ipa/libipa/awb_grey.h\n> @@ -0,0 +1,35 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024 Ideas on Board Oy\n> + *\n> + * AWB grey world algorithm\n> + */\n> +\n> +#pragma once\n> +\n> +#include \"libcamera/internal/yaml_parser.h\"\n> +\n> +#include \"awb.h\"\n> +#include \"interpolator.h\"\n> +#include \"vector.h\"\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +class AwbGrey : public AwbAlgorithm\n> +{\n> +public:\n> +\tAwbGrey() = default;\n> +\n> +\tint init(const YamlObject &tuningData) override;\n> +\tAwbResult calculateAwb(const AwbStats &stats, int lux) override;\n> +\tRGB<double> gainsFromColourTemperature(double coulourTemperature) override;\n\ns/coulourTemperature/colourTemperature\n\n\nOtherwise looks good I think:\n\n\nReviewed-by: Daniel Scally <dan.scally@ideasonboard.com>\n\n> +\n> +private:\n> +\tstd::optional<Interpolator<Vector<double, 2>>> colourGainCurve_;\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 03e879c5834f..c550a6eb45b6 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -3,6 +3,7 @@\n>   libipa_headers = files([\n>       'agc_mean_luminance.h',\n>       'algorithm.h',\n> +    'awb_grey.h',\n>       'awb.h',\n>       'camera_sensor_helper.h',\n>       'colours.h',\n> @@ -21,6 +22,7 @@ libipa_headers = files([\n>   libipa_sources = files([\n>       'agc_mean_luminance.cpp',\n>       'algorithm.cpp',\n> +    'awb_grey.cpp',\n>       'awb.cpp',\n>       'camera_sensor_helper.cpp',\n>       'colours.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 95126C3273\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 20 Jan 2025 10:37:23 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2AB226854D;\n\tMon, 20 Jan 2025 11:37:22 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id AB04268503\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Jan 2025 11:37:20 +0100 (CET)","from [192.168.0.43]\n\t(cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 28D60735\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Jan 2025 11:36: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=\"E5u6WgnD\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1737369379;\n\tbh=i+82MvK+Z1HyYh69/uu97F50UzfvOVMBG5wdqRxk7h0=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=E5u6WgnDIvbR/L0RAq16var3jKsA546xsqTOQOHBgbSjRmYS5wU/m2a1JBrg6kWaB\n\txHZsRq9pNGLYJT6CtYezhN4+MMOgCPPs+vobY43SSpqyci2hlTnAe0BN+u3ijOcLSy\n\typz1qKnW0a5AWQK+R/fhduyctVRAiU5EWQ8ydufw=","Message-ID":"<a478dd16-0273-40e4-8d41-228100f43de6@ideasonboard.com>","Date":"Mon, 20 Jan 2025 10:37:18 +0000","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","To":"libcamera-devel@lists.libcamera.org","References":"<20250109115412.356768-1-stefan.klug@ideasonboard.com>\n\t<20250109115412.356768-6-stefan.klug@ideasonboard.com>","Content-Language":"en-US","From":"Dan Scally <dan.scally@ideasonboard.com>","Autocrypt":"addr=dan.scally@ideasonboard.com; keydata=\n\t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","In-Reply-To":"<20250109115412.356768-6-stefan.klug@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","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":33099,"web_url":"https://patchwork.libcamera.org/comment/33099/","msgid":"<278e318f-0f12-4745-ae98-02fa6c96705f@ideasonboard.com>","date":"2025-01-20T11:50:55","subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","submitter":{"id":156,"url":"https://patchwork.libcamera.org/api/people/156/","name":"Dan Scally","email":"dan.scally@ideasonboard.com"},"content":"Hi Stefan\n\nOn 09/01/2025 11:53, Stefan Klug wrote:\n> Add the grey world algorithm that is currently used in rkisp1 to libipa.\n> No changes in functionality were made.\n>\n> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> ---\n>   src/ipa/libipa/awb_grey.cpp | 114 ++++++++++++++++++++++++++++++++++++\n>   src/ipa/libipa/awb_grey.h   |  35 +++++++++++\n>   src/ipa/libipa/meson.build  |   2 +\n>   3 files changed, 151 insertions(+)\n>   create mode 100644 src/ipa/libipa/awb_grey.cpp\n>   create mode 100644 src/ipa/libipa/awb_grey.h\n>\n> diff --git a/src/ipa/libipa/awb_grey.cpp b/src/ipa/libipa/awb_grey.cpp\n> new file mode 100644\n> index 000000000000..192a7cf3834a\n> --- /dev/null\n> +++ b/src/ipa/libipa/awb_grey.cpp\n> @@ -0,0 +1,114 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024 Ideas on Board Oy\n> + *\n> + * Base class for bayesian AWB algorithm\noops - just noticed here s/bayesian/grey world\n> + */\n> +\n> +#include \"awb_grey.h\"\n> +\n> +#include <cmath>\n> +\n> +#include <libcamera/base/log.h>\n> +#include <libcamera/control_ids.h>\n> +\n> +#include \"colours.h\"\n> +\n> +using namespace libcamera::controls;\n> +\n> +/**\n> + * \\file awb_grey.h\n> + * \\brief Implementation of a grey world AWB algorithm\n> + */\n> +\n> +namespace libcamera {\n> +\n> +LOG_DECLARE_CATEGORY(Awb)\n> +namespace ipa {\n> +\n> +/**\n> + * \\class AwbGrey\n> + * \\brief A Grey world auto white balance algorithm\n> + */\n> +\n> +/**\n> + * \\brief Initialize the algorithm with the given tuning data\n> + * \\param[in] tuningData The tuning data for the algorithm\n> + *\n> + * Load the colour temperature curve from the tuning data. If there is no tuning\n> + * data available, continue with a warning. Manual colour temperature will not\n> + * work in that case.\n> + *\n> + * \\return 0 on success, a negative error code otherwise\n> + */\n> +int AwbGrey::init(const YamlObject &tuningData)\n> +{\n> +\tInterpolator<Vector<double, 2>> gains;\n> +\tint ret = gains.readYaml(tuningData[\"colourGains\"], \"ct\", \"gains\");\n> +\tif (ret < 0)\n> +\t\tLOG(Awb, Warning)\n> +\t\t\t<< \"Failed to parse 'colourGains' \"\n> +\t\t\t<< \"parameter from tuning file; \"\n> +\t\t\t<< \"manual colour temperature will not work properly\";\n> +\telse\n> +\t\tcolourGainCurve_ = gains;\n> +\n> +\treturn 0;\n> +}\n> +\n> +/**\n> + * \\brief Calculate awb data from the given statistics\n> + * \\param[in] stats The statistics to use for the calculation\n> + * \\param[in] lux The lux value of the scene\n> + *\n> + * Estimates the colour temperature based on the coulours::estimateCCT function.\n> + * The gains are calculated purely based on the RGB means provided by the \\a\n> + * stats. The colour temperature is not taken into account when calculating the\n> + * gains.\n> + *\n> + * The \\a lux parameter is not used in this algorithm.\n> + *\n> + * \\return The awb result\n> + */\n> +AwbResult AwbGrey::calculateAwb(const AwbStats &stats, [[maybe_unused]] int lux)\n> +{\n> +\tAwbResult result;\n> +\tauto means = stats.getRGBMeans();\n> +\tresult.colourTemperature = estimateCCT(means);\n> +\n> +\t/*\n> +\t * Estimate the red and blue gains to apply in a grey world. The green\n> +\t * gain is hardcoded to 1.0. Avoid divisions by zero by clamping the\n> +\t * divisor to a minimum value of 1.0.\n> +\t */\n> +\tresult.gains.r() = means.g() / std::max(means.r(), 1.0);\n> +\tresult.gains.g() = 1.0;\n> +\tresult.gains.b() = means.g() / std::max(means.b(), 1.0);\n> +\treturn result;\n> +}\n> +\n> +/**\n> + * \\brief Compute white balance gains from a colour temperature\n> + * \\param[in] colourTemperature The colour temperature in Kelvin\n> + *\n> + * Compute the white balance gains from a \\a colourTemperature. This function\n> + * does not take any statistics into account. It simply interpolates the colour\n> + * gains configured in the colour temperature curve.\n> + *\n> + * \\return The colour gains if a colour temperature curve is available,\n> + * [1, 1, 1] otherwise.\n> + */\n> +RGB<double> AwbGrey::gainsFromColourTemperature(double colourTemperature)\n> +{\n> +\tif (!colourGainCurve_) {\n> +\t\tLOG(Awb, Error) << \"No gains defined\";\n> +\t\treturn RGB<double>({ 1.0, 1.0, 1.0 });\n> +\t}\n> +\n> +\tauto gains = colourGainCurve_->getInterpolated(colourTemperature);\n> +\treturn { { gains[0], 1.0, gains[0] } };\n> +}\n> +\n> +} /* namespace ipa */\n> +\n> +} /* namespace libcamera */\n> diff --git a/src/ipa/libipa/awb_grey.h b/src/ipa/libipa/awb_grey.h\n> new file mode 100644\n> index 000000000000..6eda8e5498fb\n> --- /dev/null\n> +++ b/src/ipa/libipa/awb_grey.h\n> @@ -0,0 +1,35 @@\n> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> +/*\n> + * Copyright (C) 2024 Ideas on Board Oy\n> + *\n> + * AWB grey world algorithm\n> + */\n> +\n> +#pragma once\n> +\n> +#include \"libcamera/internal/yaml_parser.h\"\n> +\n> +#include \"awb.h\"\n> +#include \"interpolator.h\"\n> +#include \"vector.h\"\n> +\n> +namespace libcamera {\n> +\n> +namespace ipa {\n> +\n> +class AwbGrey : public AwbAlgorithm\n> +{\n> +public:\n> +\tAwbGrey() = default;\n> +\n> +\tint init(const YamlObject &tuningData) override;\n> +\tAwbResult calculateAwb(const AwbStats &stats, int lux) override;\n> +\tRGB<double> gainsFromColourTemperature(double coulourTemperature) override;\n> +\n> +private:\n> +\tstd::optional<Interpolator<Vector<double, 2>>> colourGainCurve_;\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 03e879c5834f..c550a6eb45b6 100644\n> --- a/src/ipa/libipa/meson.build\n> +++ b/src/ipa/libipa/meson.build\n> @@ -3,6 +3,7 @@\n>   libipa_headers = files([\n>       'agc_mean_luminance.h',\n>       'algorithm.h',\n> +    'awb_grey.h',\n>       'awb.h',\n>       'camera_sensor_helper.h',\n>       'colours.h',\n> @@ -21,6 +22,7 @@ libipa_headers = files([\n>   libipa_sources = files([\n>       'agc_mean_luminance.cpp',\n>       'algorithm.cpp',\n> +    'awb_grey.cpp',\n>       'awb.cpp',\n>       'camera_sensor_helper.cpp',\n>       'colours.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 D13DEC327D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 20 Jan 2025 11:51:05 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id C58646854B;\n\tMon, 20 Jan 2025 12:51:04 +0100 (CET)","from perceval.ideasonboard.com (perceval.ideasonboard.com\n\t[213.167.242.64])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 4EA2460354\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Jan 2025 12:50:58 +0100 (CET)","from [192.168.0.43]\n\t(cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id CC6FF2E0\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Jan 2025 12:49:56 +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=\"gXEF7A8S\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1737373796;\n\tbh=s6L/gKHDNXi8Ed42cam8aMcNYfkbIjgp7Nxninid1Pg=;\n\th=Date:Subject:To:References:From:In-Reply-To:From;\n\tb=gXEF7A8S5/PnJTTIodoyYVgb9BxjSW11mp+D7sH53s+xVyIQ+Dy9V2Nh69eWFdNhm\n\tR17hzVwrha+dg1hkRVsT8v/8+UBXfJQN6V73t7s0HMXD+e0XidIlEjySBPPI0cFzE5\n\tfJtqQeHprAWxtEv61GeBlLl3s52aXdsWa7/sgsXg=","Message-ID":"<278e318f-0f12-4745-ae98-02fa6c96705f@ideasonboard.com>","Date":"Mon, 20 Jan 2025 11:50:55 +0000","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","To":"libcamera-devel@lists.libcamera.org","References":"<20250109115412.356768-1-stefan.klug@ideasonboard.com>\n\t<20250109115412.356768-6-stefan.klug@ideasonboard.com>","Content-Language":"en-US","From":"Dan Scally <dan.scally@ideasonboard.com>","Autocrypt":"addr=dan.scally@ideasonboard.com; keydata=\n\t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","In-Reply-To":"<20250109115412.356768-6-stefan.klug@ideasonboard.com>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","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":33101,"web_url":"https://patchwork.libcamera.org/comment/33101/","msgid":"<6huii7hntneajlrblnbscbtu5ijfusvv3yzbuyu3j42ccvi2cg@n3cqc2fki6dk>","date":"2025-01-20T13:52:30","subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","submitter":{"id":184,"url":"https://patchwork.libcamera.org/api/people/184/","name":"Stefan Klug","email":"stefan.klug@ideasonboard.com"},"content":"Hi Dan,\n\nThank you for the review. \n\nOn Mon, Jan 20, 2025 at 10:37:18AM +0000, Dan Scally wrote:\n> Hi Stefan\n> \n> On 09/01/2025 11:53, Stefan Klug wrote:\n> > Add the grey world algorithm that is currently used in rkisp1 to libipa.\n> > No changes in functionality were made.\n> > \n> > Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n> > ---\n> >   src/ipa/libipa/awb_grey.cpp | 114 ++++++++++++++++++++++++++++++++++++\n> >   src/ipa/libipa/awb_grey.h   |  35 +++++++++++\n> >   src/ipa/libipa/meson.build  |   2 +\n> >   3 files changed, 151 insertions(+)\n> >   create mode 100644 src/ipa/libipa/awb_grey.cpp\n> >   create mode 100644 src/ipa/libipa/awb_grey.h\n> > \n> > diff --git a/src/ipa/libipa/awb_grey.cpp b/src/ipa/libipa/awb_grey.cpp\n> > new file mode 100644\n> > index 000000000000..192a7cf3834a\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/awb_grey.cpp\n> > @@ -0,0 +1,114 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2024 Ideas on Board Oy\n> > + *\n> > + * Base class for bayesian AWB algorithm\n> > + */\n> > +\n> > +#include \"awb_grey.h\"\n> > +\n> > +#include <cmath>\n> > +\n> > +#include <libcamera/base/log.h>\n> > +#include <libcamera/control_ids.h>\n> > +\n> > +#include \"colours.h\"\n> > +\n> > +using namespace libcamera::controls;\n> > +\n> > +/**\n> > + * \\file awb_grey.h\n> > + * \\brief Implementation of a grey world AWB algorithm\n> > + */\n> > +\n> > +namespace libcamera {\n> > +\n> > +LOG_DECLARE_CATEGORY(Awb)\n> Is it worth differentiating between this log category and that in awb.cpp -\n> which is also just \"Awb\"? The logs will include the file path and line\n> number of course so they can be distinguished anyway so probably it doesn't\n> matter, but I thought I'd mention it just to bring it to mind - I'm happy\n> either way.\n\nI was also quite undecided here. The reason I went for the short one, is\nthat there will always be only one AWB algorithm active per camera. So\nthere seems to be no need to further differentiate that at runtime. But\nboth options are valid. I think I'd like to stick to the current\n(simple) solution for now.\n\n> > +namespace ipa {\n> > +\n> > +/**\n> > + * \\class AwbGrey\n> > + * \\brief A Grey world auto white balance algorithm\n> > + */\n> > +\n> > +/**\n> > + * \\brief Initialize the algorithm with the given tuning data\n> > + * \\param[in] tuningData The tuning data for the algorithm\n> > + *\n> > + * Load the colour temperature curve from the tuning data. If there is no tuning\n> > + * data available, continue with a warning. Manual colour temperature will not\n> > + * work in that case.\n> > + *\n> > + * \\return 0 on success, a negative error code otherwise\n> > + */\n> > +int AwbGrey::init(const YamlObject &tuningData)\n> > +{\n> > +\tInterpolator<Vector<double, 2>> gains;\n> > +\tint ret = gains.readYaml(tuningData[\"colourGains\"], \"ct\", \"gains\");\n> > +\tif (ret < 0)\n> > +\t\tLOG(Awb, Warning)\n> > +\t\t\t<< \"Failed to parse 'colourGains' \"\n> > +\t\t\t<< \"parameter from tuning file; \"\n> > +\t\t\t<< \"manual colour temperature will not work properly\";\n> > +\telse\n> > +\t\tcolourGainCurve_ = gains;\n> > +\n> > +\treturn 0;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Calculate awb data from the given statistics\n> > + * \\param[in] stats The statistics to use for the calculation\n> > + * \\param[in] lux The lux value of the scene\n> > + *\n> > + * Estimates the colour temperature based on the coulours::estimateCCT function.\n> s/coulours/colours.\n> > + * The gains are calculated purely based on the RGB means provided by the \\a\n> > + * stats. The colour temperature is not taken into account when calculating the\n> > + * gains.\n> > + *\n> > + * The \\a lux parameter is not used in this algorithm.\n> > + *\n> > + * \\return The awb result\n> > + */\n> > +AwbResult AwbGrey::calculateAwb(const AwbStats &stats, [[maybe_unused]] int lux)\n> > +{\n> > +\tAwbResult result;\n> > +\tauto means = stats.getRGBMeans();\n> > +\tresult.colourTemperature = estimateCCT(means);\n> > +\n> > +\t/*\n> > +\t * Estimate the red and blue gains to apply in a grey world. The green\n> > +\t * gain is hardcoded to 1.0. Avoid divisions by zero by clamping the\n> > +\t * divisor to a minimum value of 1.0.\n> > +\t */\n> > +\tresult.gains.r() = means.g() / std::max(means.r(), 1.0);\n> > +\tresult.gains.g() = 1.0;\n> > +\tresult.gains.b() = means.g() / std::max(means.b(), 1.0);\n> > +\treturn result;\n> > +}\n> > +\n> > +/**\n> > + * \\brief Compute white balance gains from a colour temperature\n> > + * \\param[in] colourTemperature The colour temperature in Kelvin\n> > + *\n> > + * Compute the white balance gains from a \\a colourTemperature. This function\n> > + * does not take any statistics into account. It simply interpolates the colour\n> > + * gains configured in the colour temperature curve.\n> > + *\n> > + * \\return The colour gains if a colour temperature curve is available,\n> > + * [1, 1, 1] otherwise.\n> > + */\n> > +RGB<double> AwbGrey::gainsFromColourTemperature(double colourTemperature)\n> > +{\n> > +\tif (!colourGainCurve_) {\n> > +\t\tLOG(Awb, Error) << \"No gains defined\";\n> > +\t\treturn RGB<double>({ 1.0, 1.0, 1.0 });\n> > +\t}\n> > +\n> > +\tauto gains = colourGainCurve_->getInterpolated(colourTemperature);\n> > +\treturn { { gains[0], 1.0, gains[0] } };\n> > +}\n> > +\n> > +} /* namespace ipa */\n> > +\n> > +} /* namespace libcamera */\n> > diff --git a/src/ipa/libipa/awb_grey.h b/src/ipa/libipa/awb_grey.h\n> > new file mode 100644\n> > index 000000000000..6eda8e5498fb\n> > --- /dev/null\n> > +++ b/src/ipa/libipa/awb_grey.h\n> > @@ -0,0 +1,35 @@\n> > +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n> > +/*\n> > + * Copyright (C) 2024 Ideas on Board Oy\n> > + *\n> > + * AWB grey world algorithm\n> > + */\n> > +\n> > +#pragma once\n> > +\n> > +#include \"libcamera/internal/yaml_parser.h\"\n> > +\n> > +#include \"awb.h\"\n> > +#include \"interpolator.h\"\n> > +#include \"vector.h\"\n> > +\n> > +namespace libcamera {\n> > +\n> > +namespace ipa {\n> > +\n> > +class AwbGrey : public AwbAlgorithm\n> > +{\n> > +public:\n> > +\tAwbGrey() = default;\n> > +\n> > +\tint init(const YamlObject &tuningData) override;\n> > +\tAwbResult calculateAwb(const AwbStats &stats, int lux) override;\n> > +\tRGB<double> gainsFromColourTemperature(double coulourTemperature) override;\n> \n> s/coulourTemperature/colourTemperature\n\nhmpf..\n\n> \n> \n> Otherwise looks good I think:\n> \n> \n> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>\n\nThanks,\nStefan\n\n> \n> > +\n> > +private:\n> > +\tstd::optional<Interpolator<Vector<double, 2>>> colourGainCurve_;\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 03e879c5834f..c550a6eb45b6 100644\n> > --- a/src/ipa/libipa/meson.build\n> > +++ b/src/ipa/libipa/meson.build\n> > @@ -3,6 +3,7 @@\n> >   libipa_headers = files([\n> >       'agc_mean_luminance.h',\n> >       'algorithm.h',\n> > +    'awb_grey.h',\n> >       'awb.h',\n> >       'camera_sensor_helper.h',\n> >       'colours.h',\n> > @@ -21,6 +22,7 @@ libipa_headers = files([\n> >   libipa_sources = files([\n> >       'agc_mean_luminance.cpp',\n> >       'algorithm.cpp',\n> > +    'awb_grey.cpp',\n> >       'awb.cpp',\n> >       'camera_sensor_helper.cpp',\n> >       'colours.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 D4F67C3273\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 20 Jan 2025 13:52:35 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id D90E86854B;\n\tMon, 20 Jan 2025 14:52:34 +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 3BE7D60354\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Jan 2025 14:52:33 +0100 (CET)","from ideasonboard.com (unknown\n\t[IPv6:2a00:6020:448c:6c00:4375:4b53:c7c1:47a6])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 86A4C2E0;\n\tMon, 20 Jan 2025 14: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=\"wCZOUQg0\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1737381091;\n\tbh=MWR5Z1hNC1b8DEdngQyxgqkwspi9AAUI8DF7v7l8nUI=;\n\th=Date:From:To:Cc:Subject:References:In-Reply-To:From;\n\tb=wCZOUQg0LGJpjBXHl03nAt94PIMWzENrYPpBoaIZztLzDGprrqXqviDFKfGnoC7Oa\n\t38Tlyo7WcmgkryuOV85WM7PY2zyiHZ7c64YOBxtYlVaJyXKxmujCN+MbyllgHWfGva\n\t2A5dja2tTXLxdIshezmNEBffaKQHiPVm1iFQcv2U=","Date":"Mon, 20 Jan 2025 14:52:30 +0100","From":"Stefan Klug <stefan.klug@ideasonboard.com>","To":"Dan Scally <dan.scally@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","Subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","Message-ID":"<6huii7hntneajlrblnbscbtu5ijfusvv3yzbuyu3j42ccvi2cg@n3cqc2fki6dk>","References":"<20250109115412.356768-1-stefan.klug@ideasonboard.com>\n\t<20250109115412.356768-6-stefan.klug@ideasonboard.com>\n\t<a478dd16-0273-40e4-8d41-228100f43de6@ideasonboard.com>","MIME-Version":"1.0","Content-Type":"text/plain; charset=utf-8","Content-Disposition":"inline","In-Reply-To":"<a478dd16-0273-40e4-8d41-228100f43de6@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>"}},{"id":33106,"web_url":"https://patchwork.libcamera.org/comment/33106/","msgid":"<79297409-2c95-4802-87c4-f957484cce9f@ideasonboard.com>","date":"2025-01-20T15:51:08","subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","submitter":{"id":156,"url":"https://patchwork.libcamera.org/api/people/156/","name":"Dan Scally","email":"dan.scally@ideasonboard.com"},"content":"Hi Stefan\n\nOn 20/01/2025 13:52, Stefan Klug wrote:\n> Hi Dan,\n>\n> Thank you for the review.\n>\n> On Mon, Jan 20, 2025 at 10:37:18AM +0000, Dan Scally wrote:\n>> Hi Stefan\n>>\n>> On 09/01/2025 11:53, Stefan Klug wrote:\n>>> Add the grey world algorithm that is currently used in rkisp1 to libipa.\n>>> No changes in functionality were made.\n>>>\n>>> Signed-off-by: Stefan Klug <stefan.klug@ideasonboard.com>\n>>> ---\n>>>    src/ipa/libipa/awb_grey.cpp | 114 ++++++++++++++++++++++++++++++++++++\n>>>    src/ipa/libipa/awb_grey.h   |  35 +++++++++++\n>>>    src/ipa/libipa/meson.build  |   2 +\n>>>    3 files changed, 151 insertions(+)\n>>>    create mode 100644 src/ipa/libipa/awb_grey.cpp\n>>>    create mode 100644 src/ipa/libipa/awb_grey.h\n>>>\n>>> diff --git a/src/ipa/libipa/awb_grey.cpp b/src/ipa/libipa/awb_grey.cpp\n>>> new file mode 100644\n>>> index 000000000000..192a7cf3834a\n>>> --- /dev/null\n>>> +++ b/src/ipa/libipa/awb_grey.cpp\n>>> @@ -0,0 +1,114 @@\n>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>>> +/*\n>>> + * Copyright (C) 2024 Ideas on Board Oy\n>>> + *\n>>> + * Base class for bayesian AWB algorithm\n>>> + */\n>>> +\n>>> +#include \"awb_grey.h\"\n>>> +\n>>> +#include <cmath>\n>>> +\n>>> +#include <libcamera/base/log.h>\n>>> +#include <libcamera/control_ids.h>\n>>> +\n>>> +#include \"colours.h\"\n>>> +\n>>> +using namespace libcamera::controls;\n>>> +\n>>> +/**\n>>> + * \\file awb_grey.h\n>>> + * \\brief Implementation of a grey world AWB algorithm\n>>> + */\n>>> +\n>>> +namespace libcamera {\n>>> +\n>>> +LOG_DECLARE_CATEGORY(Awb)\n>> Is it worth differentiating between this log category and that in awb.cpp -\n>> which is also just \"Awb\"? The logs will include the file path and line\n>> number of course so they can be distinguished anyway so probably it doesn't\n>> matter, but I thought I'd mention it just to bring it to mind - I'm happy\n>> either way.\n> I was also quite undecided here. The reason I went for the short one, is\n> that there will always be only one AWB algorithm active per camera. So\n> there seems to be no need to further differentiate that at runtime.\nThat seems like a solid argument to me - also makes it easier to remember which to turn on I suppose.\n>   But\n> both options are valid. I think I'd like to stick to the current\n> (simple) solution for now.\nAlright - fine by me.\n>\n>>> +namespace ipa {\n>>> +\n>>> +/**\n>>> + * \\class AwbGrey\n>>> + * \\brief A Grey world auto white balance algorithm\n>>> + */\n>>> +\n>>> +/**\n>>> + * \\brief Initialize the algorithm with the given tuning data\n>>> + * \\param[in] tuningData The tuning data for the algorithm\n>>> + *\n>>> + * Load the colour temperature curve from the tuning data. If there is no tuning\n>>> + * data available, continue with a warning. Manual colour temperature will not\n>>> + * work in that case.\n>>> + *\n>>> + * \\return 0 on success, a negative error code otherwise\n>>> + */\n>>> +int AwbGrey::init(const YamlObject &tuningData)\n>>> +{\n>>> +\tInterpolator<Vector<double, 2>> gains;\n>>> +\tint ret = gains.readYaml(tuningData[\"colourGains\"], \"ct\", \"gains\");\n>>> +\tif (ret < 0)\n>>> +\t\tLOG(Awb, Warning)\n>>> +\t\t\t<< \"Failed to parse 'colourGains' \"\n>>> +\t\t\t<< \"parameter from tuning file; \"\n>>> +\t\t\t<< \"manual colour temperature will not work properly\";\n>>> +\telse\n>>> +\t\tcolourGainCurve_ = gains;\n>>> +\n>>> +\treturn 0;\n>>> +}\n>>> +\n>>> +/**\n>>> + * \\brief Calculate awb data from the given statistics\n>>> + * \\param[in] stats The statistics to use for the calculation\n>>> + * \\param[in] lux The lux value of the scene\n>>> + *\n>>> + * Estimates the colour temperature based on the coulours::estimateCCT function.\n>> s/coulours/colours.\n>>> + * The gains are calculated purely based on the RGB means provided by the \\a\n>>> + * stats. The colour temperature is not taken into account when calculating the\n>>> + * gains.\n>>> + *\n>>> + * The \\a lux parameter is not used in this algorithm.\n>>> + *\n>>> + * \\return The awb result\n>>> + */\n>>> +AwbResult AwbGrey::calculateAwb(const AwbStats &stats, [[maybe_unused]] int lux)\n>>> +{\n>>> +\tAwbResult result;\n>>> +\tauto means = stats.getRGBMeans();\n>>> +\tresult.colourTemperature = estimateCCT(means);\n>>> +\n>>> +\t/*\n>>> +\t * Estimate the red and blue gains to apply in a grey world. The green\n>>> +\t * gain is hardcoded to 1.0. Avoid divisions by zero by clamping the\n>>> +\t * divisor to a minimum value of 1.0.\n>>> +\t */\n>>> +\tresult.gains.r() = means.g() / std::max(means.r(), 1.0);\n>>> +\tresult.gains.g() = 1.0;\n>>> +\tresult.gains.b() = means.g() / std::max(means.b(), 1.0);\n>>> +\treturn result;\n>>> +}\n>>> +\n>>> +/**\n>>> + * \\brief Compute white balance gains from a colour temperature\n>>> + * \\param[in] colourTemperature The colour temperature in Kelvin\n>>> + *\n>>> + * Compute the white balance gains from a \\a colourTemperature. This function\n>>> + * does not take any statistics into account. It simply interpolates the colour\n>>> + * gains configured in the colour temperature curve.\n>>> + *\n>>> + * \\return The colour gains if a colour temperature curve is available,\n>>> + * [1, 1, 1] otherwise.\n>>> + */\n>>> +RGB<double> AwbGrey::gainsFromColourTemperature(double colourTemperature)\n>>> +{\n>>> +\tif (!colourGainCurve_) {\n>>> +\t\tLOG(Awb, Error) << \"No gains defined\";\n>>> +\t\treturn RGB<double>({ 1.0, 1.0, 1.0 });\n>>> +\t}\n>>> +\n>>> +\tauto gains = colourGainCurve_->getInterpolated(colourTemperature);\n>>> +\treturn { { gains[0], 1.0, gains[0] } };\n>>> +}\n>>> +\n>>> +} /* namespace ipa */\n>>> +\n>>> +} /* namespace libcamera */\n>>> diff --git a/src/ipa/libipa/awb_grey.h b/src/ipa/libipa/awb_grey.h\n>>> new file mode 100644\n>>> index 000000000000..6eda8e5498fb\n>>> --- /dev/null\n>>> +++ b/src/ipa/libipa/awb_grey.h\n>>> @@ -0,0 +1,35 @@\n>>> +/* SPDX-License-Identifier: LGPL-2.1-or-later */\n>>> +/*\n>>> + * Copyright (C) 2024 Ideas on Board Oy\n>>> + *\n>>> + * AWB grey world algorithm\n>>> + */\n>>> +\n>>> +#pragma once\n>>> +\n>>> +#include \"libcamera/internal/yaml_parser.h\"\n>>> +\n>>> +#include \"awb.h\"\n>>> +#include \"interpolator.h\"\n>>> +#include \"vector.h\"\n>>> +\n>>> +namespace libcamera {\n>>> +\n>>> +namespace ipa {\n>>> +\n>>> +class AwbGrey : public AwbAlgorithm\n>>> +{\n>>> +public:\n>>> +\tAwbGrey() = default;\n>>> +\n>>> +\tint init(const YamlObject &tuningData) override;\n>>> +\tAwbResult calculateAwb(const AwbStats &stats, int lux) override;\n>>> +\tRGB<double> gainsFromColourTemperature(double coulourTemperature) override;\n>> s/coulourTemperature/colourTemperature\n> hmpf..\n>\n>>\n>> Otherwise looks good I think:\n>>\n>>\n>> Reviewed-by: Daniel Scally <dan.scally@ideasonboard.com>\n> Thanks,\n> Stefan\n>\n>>> +\n>>> +private:\n>>> +\tstd::optional<Interpolator<Vector<double, 2>>> colourGainCurve_;\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 03e879c5834f..c550a6eb45b6 100644\n>>> --- a/src/ipa/libipa/meson.build\n>>> +++ b/src/ipa/libipa/meson.build\n>>> @@ -3,6 +3,7 @@\n>>>    libipa_headers = files([\n>>>        'agc_mean_luminance.h',\n>>>        'algorithm.h',\n>>> +    'awb_grey.h',\n>>>        'awb.h',\n>>>        'camera_sensor_helper.h',\n>>>        'colours.h',\n>>> @@ -21,6 +22,7 @@ libipa_headers = files([\n>>>    libipa_sources = files([\n>>>        'agc_mean_luminance.cpp',\n>>>        'algorithm.cpp',\n>>> +    'awb_grey.cpp',\n>>>        'awb.cpp',\n>>>        'camera_sensor_helper.cpp',\n>>>        'colours.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 05A10C327D\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon, 20 Jan 2025 15:51:12 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id B200E6854E;\n\tMon, 20 Jan 2025 16:51:11 +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 090D760354\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 20 Jan 2025 16:51:11 +0100 (CET)","from [192.168.0.43]\n\t(cpc141996-chfd3-2-0-cust928.12-3.cable.virginm.net [86.13.91.161])\n\tby perceval.ideasonboard.com (Postfix) with ESMTPSA id 8FCB41E3;\n\tMon, 20 Jan 2025 16:50:09 +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=\"Y6fm9hdp\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com;\n\ts=mail; t=1737388209;\n\tbh=m264rMy9ZMp1bL/808ZFpbhlJh2u3mYjxob1w0/RgAo=;\n\th=Date:Subject:To:Cc:References:From:In-Reply-To:From;\n\tb=Y6fm9hdpQ4LYdi7cn43+MgT62AcRaI4folOCStj5XUEhfNnMCsGavAk9fDYAgAIv/\n\tInVmr+9rUfWdMaVaGF7NPa+cTZPJSCIzqq+lDTkHYrTqhf1JygbDNLrvJiisIcB0Qz\n\tMpW8sTbLw0+9pYltdD5l7OuWiFekTjDKWVfpQ+zM=","Message-ID":"<79297409-2c95-4802-87c4-f957484cce9f@ideasonboard.com>","Date":"Mon, 20 Jan 2025 15:51:08 +0000","MIME-Version":"1.0","User-Agent":"Mozilla Thunderbird","Subject":"Re: [PATCH v1 05/11] libipa: Add grey world AWB algorithm","To":"Stefan Klug <stefan.klug@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org","References":"<20250109115412.356768-1-stefan.klug@ideasonboard.com>\n\t<20250109115412.356768-6-stefan.klug@ideasonboard.com>\n\t<a478dd16-0273-40e4-8d41-228100f43de6@ideasonboard.com>\n\t<6huii7hntneajlrblnbscbtu5ijfusvv3yzbuyu3j42ccvi2cg@n3cqc2fki6dk>","Content-Language":"en-US","From":"Dan Scally <dan.scally@ideasonboard.com>","Autocrypt":"addr=dan.scally@ideasonboard.com; keydata=\n\t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","In-Reply-To":"<6huii7hntneajlrblnbscbtu5ijfusvv3yzbuyu3j42ccvi2cg@n3cqc2fki6dk>","Content-Type":"text/plain; charset=UTF-8; format=flowed","Content-Transfer-Encoding":"7bit","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>"}}]