[{"id":40064,"web_url":"https://patchwork.libcamera.org/comment/40064/","msgid":"<85ecgc2yj0.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","date":"2026-08-05T18:49:23","subject":"Re: [PATCH v7 03/32] ipa: simple: awb: Port to use libipa\n\tAwbAlgorithm","submitter":{"id":177,"url":"https://patchwork.libcamera.org/api/people/177/","name":"Milan Zamazal","email":"mzamazal@redhat.com"},"content":"Jacopo Mondi <jacopo.mondi@ideasonboard.com> writes:\n\n> From: Kieran Bingham <kieran.bingham@ideasonboard.com>\n>\n> Port the SoftISP Awb algorithm to use the new libipa implementation\n> of AwbAlgorithm.\n>\n> The awbAlgo_ class member is initialized with the Q<2, 8> type even if\n> there is no physical register representation for SoftISP.\n>\n> The usage of libipa::awb::Context, which defines the gains vector as\n> RGB<double>, in the SimpleIPA ActiveState and FrameContext requires\n> changes to debayer_cpu.cpp and blc.cpp in order not to mix RGB<float>\n> and RGB<double>.\n>\n> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> Signed-off-by: Jacopo Mondi <jacopo.mondi@ideasonboard.com>\n> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>\n> Tested-by: Robert Mader <robert.mader@collabora.com>\n> Tested-by: Milan Zamazal <mzamazal@redhat.com>\n\nReviewed-by: Milan Zamazal <mzamazal@redhat.com>\n\n> ---\n> v5->v6:\n> - Remove [[maybe_unused]] from header\n> - cast sum.r()/b()/g() to double before averaging\n> - remove unused include for colors.h\n> - remove \\todo about means\n> ---\n>  .../internal/software_isp/debayer_params.h         |   4 +-\n>  src/ipa/simple/algorithms/awb.cpp                  | 140 +++++++++++++++------\n>  src/ipa/simple/algorithms/awb.h                    |  28 +++++\n>  src/ipa/simple/algorithms/blc.cpp                  |   2 +-\n>  src/ipa/simple/algorithms/ccm.cpp                  |   2 +-\n>  src/ipa/simple/ipa_context.h                       |  12 +-\n>  src/libcamera/software_isp/debayer_cpu.cpp         |  12 +-\n>  7 files changed, 145 insertions(+), 55 deletions(-)\n>\n> diff --git a/include/libcamera/internal/software_isp/debayer_params.h b/include/libcamera/internal/software_isp/debayer_params.h\n> index 6772b43bced4..1074720d73c2 100644\n> --- a/include/libcamera/internal/software_isp/debayer_params.h\n> +++ b/include/libcamera/internal/software_isp/debayer_params.h\n> @@ -21,10 +21,10 @@ struct DebayerParams {\n>  \tMatrix<float, 3, 3> combinedMatrix = { { 1.0, 0.0, 0.0,\n>  \t\t\t\t\t\t 0.0, 1.0, 0.0,\n>  \t\t\t\t\t\t 0.0, 0.0, 1.0 } };\n> -\tRGB<float> blackLevel = RGB<float>({ 0.0, 0.0, 0.0 });\n> +\tRGB<double> blackLevel = RGB<double>({ 0.0, 0.0, 0.0 });\n>  \tfloat gamma = 1.0;\n>  \tfloat contrastExp = 1.0;\n> -\tRGB<float> gains = RGB<float>({ 1.0, 1.0, 1.0 });\n> +\tRGB<double> gains = RGB<double>({ 1.0, 1.0, 1.0 });\n>  };\n>  \n>  } /* namespace libcamera */\n> diff --git a/src/ipa/simple/algorithms/awb.cpp b/src/ipa/simple/algorithms/awb.cpp\n> index 05155c83d172..0bd6b66ce859 100644\n> --- a/src/ipa/simple/algorithms/awb.cpp\n> +++ b/src/ipa/simple/algorithms/awb.cpp\n> @@ -14,50 +14,113 @@\n>  \n>  #include <libcamera/control_ids.h>\n>  \n> -#include \"libipa/colours.h\"\n> -#include \"simple/ipa_context.h\"\n> -\n>  namespace libcamera {\n>  \n>  LOG_DEFINE_CATEGORY(IPASoftAwb)\n>  \n>  namespace ipa::soft::algorithms {\n>  \n> +/*\n> + * \\todo Replace it with a proper Lux algorithm\n> + */\n> +static constexpr unsigned int kDefaultLux = 500;\n> +\n> +class SimpleAwbStats final : public AwbStats\n> +{\n> +public:\n> +\tSimpleAwbStats() = default;\n> +\n> +\tSimpleAwbStats(const RGB<double> &rgbMeans)\n> +\t{\n> +\t\trgbMeans_ = rgbMeans;\n> +\n> +\t\trg_ = rgbMeans_.r() / rgbMeans_.g();\n> +\t\tbg_ = rgbMeans_.b() / rgbMeans_.g();\n> +\t}\n> +\n> +\tdouble computeColourError(const RGB<double> &gains) const override\n> +\t{\n> +\t\t/*\n> +\t\t * Compute the sum of the squared colour error (non-greyness) as\n> +\t\t * it appears in the log likelihood equation.\n> +\t\t */\n> +\t\tdouble deltaR = gains.r() * rg_ - 1.0;\n> +\t\tdouble deltaB = gains.b() * bg_ - 1.0;\n> +\t\tdouble delta2 = deltaR * deltaR + deltaB * deltaB;\n> +\n> +\t\treturn delta2;\n> +\t}\n> +\n> +\tRGB<double> rgbMeans() const override\n> +\t{\n> +\t\treturn rgbMeans_;\n> +\t}\n> +\n> +\tbool valid() const override\n> +\t{\n> +\t\t/* Minimum mean value below which AWB can't operate. */\n> +\t\tconstexpr double minValue = 0.2;\n> +\n> +\t\treturn rgbMeans_.r() > minValue || rgbMeans_.g() > minValue ||\n> +\t\t       rgbMeans_.b() > minValue;\n> +\t}\n> +\n> +private:\n> +\tRGB<double> rgbMeans_;\n> +\tdouble rg_;\n> +\tdouble bg_;\n> +};\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::init\n> + */\n> +int Awb::init(IPAContext &context, const ValueNode &tuningData)\n> +{\n> +\treturn awbAlgo_.init(tuningData, context.ctrlMap);\n> +}\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::configure\n> + */\n>  int Awb::configure(IPAContext &context,\n>  \t\t   [[maybe_unused]] const IPAConfigInfo &configInfo)\n>  {\n> -\tauto &gains = context.activeState.awb.gains;\n> -\tgains = { { 1.0, 1.0, 1.0 } };\n> +\treturn awbAlgo_.configure(context.activeState.awb);\n> +}\n>  \n> -\treturn 0;\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::queueRequest\n> + */\n> +void Awb::queueRequest(IPAContext &context, const uint32_t frame,\n> +\t\t       IPAFrameContext &frameContext,\n> +\t\t       const ControlList &controls)\n> +{\n> +\tawbAlgo_.queueRequest(context.activeState.awb, frame, frameContext.awb,\n> +\t\t\t      controls);\n>  }\n>  \n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::prepare\n> + */\n>  void Awb::prepare(IPAContext &context,\n>  \t\t  [[maybe_unused]] const uint32_t frame,\n>  \t\t  IPAFrameContext &frameContext,\n>  \t\t  DebayerParams *params)\n>  {\n> -\tauto &gains = context.activeState.awb.gains;\n> +\tawbAlgo_.prepare(context.activeState.awb, frameContext.awb);\n>  \n> -\tframeContext.gains = gains;\n> -\tparams->gains = gains;\n> +\tparams->gains = frameContext.awb.gains;\n>  }\n>  \n> -void Awb::process(IPAContext &context,\n> -\t\t  [[maybe_unused]] const uint32_t frame,\n> -\t\t  IPAFrameContext &frameContext,\n> -\t\t  const SwIspStats *stats,\n> -\t\t  ControlList &metadata)\n> +SimpleAwbStats Awb::calculateRgbMeans(IPAContext &context,\n> +\t\t\t\t      const SwIspStats *stats) const\n>  {\n> +\tif (!stats->valid)\n> +\t\treturn {};\n> +\n>  \tconst SwIspStats::Histogram &histogram = stats->yHistogram;\n>  \tconst uint8_t blackLevel = context.activeState.blc.level;\n>  \n> -\tmetadata.set(controls::ColourGains, { frameContext.gains.r(),\n> -\t\t\t\t\t      frameContext.gains.b() });\n> -\n> -\tif (!stats->valid)\n> -\t\treturn;\n> -\n>  \t/*\n>  \t * Black level must be subtracted to get the correct AWB ratios, they\n>  \t * would be off if they were computed from the whole brightness range\n> @@ -67,30 +130,31 @@ void Awb::process(IPAContext &context,\n>  \t\thistogram.begin(), histogram.end(), uint64_t(0));\n>  \tconst uint64_t offset = blackLevel * nPixels;\n>  \tconst uint64_t minValid = 1;\n> +\n>  \t/*\n>  \t * Make sure the sums are at least minValid, while preventing unsigned\n>  \t * integer underflow.\n>  \t */\n>  \tconst RGB<uint64_t> sum = stats->sum_.max(offset + minValid) - offset;\n>  \n> -\t/*\n> -\t * Calculate red and blue gains for AWB.\n> -\t * Clamp max gain at 4.0, this also avoids 0 division.\n> -\t */\n> -\tauto &gains = context.activeState.awb.gains;\n> -\tgains = { {\n> -\t\tsum.r() <= sum.g() / 4 ? 4.0f : static_cast<float>(sum.g()) / sum.r(),\n> -\t\t1.0,\n> -\t\tsum.b() <= sum.g() / 4 ? 4.0f : static_cast<float>(sum.g()) / sum.b(),\n> -\t} };\n> -\n> -\tRGB<double> rgbGains{ { 1 / gains.r(), 1 / gains.g(), 1 / gains.b() } };\n> -\tcontext.activeState.awb.temperatureK = estimateCCT(rgbGains);\n> -\tmetadata.set(controls::ColourTemperature, context.activeState.awb.temperatureK);\n> -\n> -\tLOG(IPASoftAwb, Debug)\n> -\t\t<< \"gain R/B: \" << gains << \"; temperature: \"\n> -\t\t<< context.activeState.awb.temperatureK;\n> +\tRGB<double> rgbMeans = { { static_cast<double>(sum.r()) / nPixels,\n> +\t\t\t\t   static_cast<double>(sum.g()) / nPixels,\n> +\t\t\t\t   static_cast<double>(sum.b()) / nPixels } };\n> +\n> +\treturn SimpleAwbStats(rgbMeans);\n> +}\n> +\n> +/**\n> + * \\copydoc libcamera::ipa::Algorithm::process\n> + */\n> +void Awb::process(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n> +\t\t  IPAFrameContext &frameContext, const SwIspStats *stats,\n> +\t\t  ControlList &metadata)\n> +{\n> +\tSimpleAwbStats awbStats = calculateRgbMeans(context, stats);\n> +\n> +\tawbAlgo_.process(context.activeState.awb, frameContext.awb, awbStats,\n> +\t\t\t kDefaultLux, metadata);\n>  }\n>  \n>  REGISTER_IPA_ALGORITHM(Awb, \"Awb\")\n> diff --git a/src/ipa/simple/algorithms/awb.h b/src/ipa/simple/algorithms/awb.h\n> index ad993f39c180..ff0a7a28e8f2 100644\n> --- a/src/ipa/simple/algorithms/awb.h\n> +++ b/src/ipa/simple/algorithms/awb.h\n> @@ -7,19 +7,36 @@\n>  \n>  #pragma once\n>  \n> +#include <libcamera/controls.h>\n> +\n> +#include \"libcamera/internal/software_isp/debayer_params.h\"\n> +#include \"libcamera/internal/value_node.h\"\n> +\n> +#include \"libipa/awb.h\"\n> +#include \"libipa/fixedpoint.h\"\n> +#include \"simple/ipa_context.h\"\n> +\n>  #include \"algorithm.h\"\n>  \n>  namespace libcamera {\n>  \n>  namespace ipa::soft::algorithms {\n>  \n> +class SimpleAwbStats;\n> +\n>  class Awb : public Algorithm\n>  {\n>  public:\n>  \tAwb() = default;\n>  \t~Awb() = default;\n>  \n> +\tint init(IPAContext &context,\n> +\t\t const ValueNode &tuningData) override;\n>  \tint configure(IPAContext &context, const IPAConfigInfo &configInfo) override;\n> +\n> +\tvoid queueRequest(IPAContext &context, const uint32_t frame,\n> +\t\t\t  IPAFrameContext &frameContext,\n> +\t\t\t  const ControlList &controls) override;\n>  \tvoid prepare(IPAContext &context,\n>  \t\t     const uint32_t frame,\n>  \t\t     IPAFrameContext &frameContext,\n> @@ -29,6 +46,17 @@ public:\n>  \t\t     IPAFrameContext &frameContext,\n>  \t\t     const SwIspStats *stats,\n>  \t\t     ControlList &metadata) override;\n> +\n> +private:\n> +\tSimpleAwbStats calculateRgbMeans(IPAContext &context,\n> +\t\t\t\t\t const SwIspStats *stats) const;\n> +\n> +\t/*\n> +\t * There actually is no Q register format for SoftISP, but allow the\n> +\t * colour gains to range in the [0.0f, 3.999f] interval, which seems\n> +\t * reasonable.\n> +\t */\n> +\tAwbAlgorithm<UQ<2, 8>> awbAlgo_;\n>  };\n>  \n>  } /* namespace ipa::soft::algorithms */\n> diff --git a/src/ipa/simple/algorithms/blc.cpp b/src/ipa/simple/algorithms/blc.cpp\n> index 677be56ed669..e45a913cd970 100644\n> --- a/src/ipa/simple/algorithms/blc.cpp\n> +++ b/src/ipa/simple/algorithms/blc.cpp\n> @@ -53,7 +53,7 @@ void BlackLevel::prepare(IPAContext &context,\n>  \t\t\t DebayerParams *params)\n>  {\n>  \t/* Latch the blacklevel gain so GPUISP can apply. */\n> -\tparams->blackLevel = RGB<float>(context.activeState.blc.level / 255.0f);\n> +\tparams->blackLevel = RGB<double>(context.activeState.blc.level / 255.0f);\n>  }\n>  \n>  void BlackLevel::process(IPAContext &context,\n> diff --git a/src/ipa/simple/algorithms/ccm.cpp b/src/ipa/simple/algorithms/ccm.cpp\n> index ace9c35dc462..1174784edc7e 100644\n> --- a/src/ipa/simple/algorithms/ccm.cpp\n> +++ b/src/ipa/simple/algorithms/ccm.cpp\n> @@ -44,7 +44,7 @@ int Ccm::init([[maybe_unused]] IPAContext &context, const ValueNode &tuningData)\n>  void Ccm::prepare(IPAContext &context, [[maybe_unused]] const uint32_t frame,\n>  \t\t  IPAFrameContext &frameContext, [[maybe_unused]] DebayerParams *params)\n>  {\n> -\tconst unsigned int ct = context.activeState.awb.temperatureK;\n> +\tconst unsigned int ct = frameContext.awb.colourTemperature;\n>  \n>  \t/* Change CCM only on bigger temperature changes. */\n>  \tif (!currentCcm_ ||\n> diff --git a/src/ipa/simple/ipa_context.h b/src/ipa/simple/ipa_context.h\n> index 8ccfacb46a59..29643a655ce1 100644\n> --- a/src/ipa/simple/ipa_context.h\n> +++ b/src/ipa/simple/ipa_context.h\n> @@ -16,6 +16,7 @@\n>  #include \"libcamera/internal/matrix.h\"\n>  #include \"libcamera/internal/vector.h\"\n>  \n> +#include <libipa/awb.h>\n>  #include <libipa/fc_queue.h>\n>  \n>  #include \"core_ipa_interface.h\"\n> @@ -36,6 +37,8 @@ struct IPASessionConfiguration {\n>  };\n>  \n>  struct IPAActiveState {\n> +\tipa::awb::ActiveState awb;\n> +\n>  \tstruct {\n>  \t\tint32_t exposure;\n>  \t\tdouble again;\n> @@ -48,11 +51,6 @@ struct IPAActiveState {\n>  \t\tdouble lastGain;\n>  \t} blc;\n>  \n> -\tstruct {\n> -\t\tRGB<float> gains;\n> -\t\tunsigned int temperatureK;\n> -\t} awb;\n> -\n>  \tMatrix<float, 3, 3> combinedMatrix;\n>  \n>  \tstruct {\n> @@ -64,6 +62,8 @@ struct IPAActiveState {\n>  };\n>  \n>  struct IPAFrameContext : public FrameContext {\n> +\tipa::awb::FrameContext awb;\n> +\n>  \tMatrix<float, 3, 3> ccm;\n>  \n>  \tstruct {\n> @@ -71,8 +71,6 @@ struct IPAFrameContext : public FrameContext {\n>  \t\tdouble gain;\n>  \t} sensor;\n>  \n> -\tRGB<float> gains;\n> -\n>  \tfloat gamma;\n>  \tstd::optional<float> contrast;\n>  \tstd::optional<float> saturation;\n> diff --git a/src/libcamera/software_isp/debayer_cpu.cpp b/src/libcamera/software_isp/debayer_cpu.cpp\n> index 25e9830c8a16..c6d5d1e1813b 100644\n> --- a/src/libcamera/software_isp/debayer_cpu.cpp\n> +++ b/src/libcamera/software_isp/debayer_cpu.cpp\n> @@ -987,9 +987,9 @@ void DebayerCpu::updateLookupTables(const DebayerParams &params)\n>  \t};\n>  \tconst unsigned int gammaTableSize = gammaTable_.size();\n>  \n> -\tconst RGB<float> blackIndex = params.blackLevel * kRGBLookupSize;\n> -\tconst RGB<float> gains = params.gains;\n> -\tconst RGB<float> div = (RGB<float>(kRGBLookupSize) - blackIndex).max(1.0);\n> +\tconst RGB<double> blackIndex = params.blackLevel * kRGBLookupSize;\n> +\tconst RGB<double> gains = params.gains;\n> +\tconst RGB<double> div = (RGB<double>(kRGBLookupSize) - blackIndex).max(1.0);\n>  \n>  \tif (ccmEnabled_) {\n>  \t\tif (gammaUpdateNeeded ||\n> @@ -1002,7 +1002,7 @@ void DebayerCpu::updateLookupTables(const DebayerParams &params)\n>  \t\t\tconst unsigned int greenIndex = 1;\n>  \t\t\tconst unsigned int blueIndex = swapRedBlueGains_ ? 0 : 2;\n>  \t\t\tfor (unsigned int i = 0; i < kRGBLookupSize; i++) {\n> -\t\t\t\tconst RGB<float> rgb = (gains * (RGB<float>(i) - blackIndex) * kRGBLookupSize / div)\n> +\t\t\t\tconst RGB<double> rgb = (gains * (RGB<double>(i) - blackIndex) * kRGBLookupSize / div)\n>  \t\t\t\t\t\t\t       .clamp(0.0, kRGBLookupSize - 1);\n>  \t\t\t\tred[i].r = std::round(rgb.r() * params.combinedMatrix[redIndex][0]);\n>  \t\t\t\tred[i].g = std::round(rgb.r() * params.combinedMatrix[greenIndex][0]);\n> @@ -1022,8 +1022,8 @@ void DebayerCpu::updateLookupTables(const DebayerParams &params)\n>  \t\t\tauto &green = green_;\n>  \t\t\tauto &blue = swapRedBlueGains_ ? red_ : blue_;\n>  \t\t\tfor (unsigned int i = 0; i < kRGBLookupSize; i++) {\n> -\t\t\t\tconst RGB<float> lutGains =\n> -\t\t\t\t\t(gains * (RGB<float>(i) - blackIndex) * gammaTableSize / div)\n> +\t\t\t\tconst RGB<double> lutGains =\n> +\t\t\t\t\t(gains * (RGB<double>(i) - blackIndex) * gammaTableSize / div)\n>  \t\t\t\t\t\t.clamp(0.0, gammaTableSize - 1);\n>  \t\t\t\tred[i] = gammaTable_[lutGains.r()];\n>  \t\t\t\tgreen[i] = gammaTable_[lutGains.g()];","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 DFB19C3301\n\tfor <parsemail@patchwork.libcamera.org>;\n\tWed,  5 Aug 2026 18:49:32 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 2F42F68134;\n\tWed,  5 Aug 2026 20:49:32 +0200 (CEST)","from us-smtp-delivery-124.mimecast.com\n\t(us-smtp-delivery-124.mimecast.com [170.10.133.124])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id 1B99D68124\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed,  5 Aug 2026 20:49:30 +0200 (CEST)","from mail-wr1-f69.google.com (mail-wr1-f69.google.com\n\t[209.85.221.69]) by relay.mimecast.com with ESMTP with STARTTLS\n\t(version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id\n\tus-mta-574-Mfx_MH2XMU2X39hQCSHJbg-1; Wed, 05 Aug 2026 14:49:27 -0400","by mail-wr1-f69.google.com with SMTP id\n\tffacd0b85a97d-47f2de3ba47so735035f8f.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tWed, 05 Aug 2026 11:49:27 -0700 (PDT)","from mzamazal-thinkpadp1gen7.tpbc.csb\n\t(ip-77-48-47-4.net.vodafone.cz. [77.48.47.4])\n\tby smtp.gmail.com with ESMTPSA id\n\tffacd0b85a97d-47febfda11csm10703674f8f.1.2026.08.05.11.49.24\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tWed, 05 Aug 2026 11:49:24 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (1024-bit key;\n\tunprotected) header.d=redhat.com header.i=@redhat.com\n\theader.b=\"R911Jhwf\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n\ts=mimecast20190719; t=1785955769;\n\th=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n\tto:to:cc:cc:mime-version:mime-version:content-type:content-type:\n\tin-reply-to:in-reply-to:references:references;\n\tbh=pgXrRb2yeByjhOLjUKLvnV/r9Z5LRDD36+EnVGMfznE=;\n\tb=R911JhwfwqlZNl0HFfQqCnsDsKFFtvaxkE5wQNNfffUeDAf//KrVBzLTKFUNNE2PJnpxyS\n\trqoAiL9xyU39ns0Xq4up5ytb6z+xibQP4G0kI3YSUgdj+ENHQtp4fkMMC+NUdBDAwtIcLw\n\tiRKSkAfRnhCCl8zkJiGkrn+P/HYlQk0=","X-MC-Unique":"Mfx_MH2XMU2X39hQCSHJbg-1","X-Mimecast-MFC-AGG-ID":"Mfx_MH2XMU2X39hQCSHJbg_1785955766","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20251104; t=1785955766; x=1786560566;\n\th=content-type:mime-version:user-agent:message-id:date:references\n\t:in-reply-to:subject:cc:to:from:x-gm-gg:x-gm-message-state:from:to\n\t:cc:subject:date:message-id:reply-to:content-type;\n\tbh=pgXrRb2yeByjhOLjUKLvnV/r9Z5LRDD36+EnVGMfznE=;\n\tb=CIL32EYMciXuCM5VZ3dMCQKsMoR+/NxIgE0+MOzJhUNmKo+n3Ds4n8EA0B1sD6tABX\n\twDVsuhPBjq2u098n21M92VGTkTGaTrhnWSiw0UqAkAqr0nftTlMHiV8g3FeeMy5z0kW2\n\ter1176d8XPCKC069u4uGm90f934A4S/f9ucwjyVZjBUKmKpyncP16Xryo4P9XOMfXt6J\n\t7tlHrhVAnUtriYB7ewi3RzGAejeX5X9xmTF9eKE61pseLNdJylHFEaBU3I/PRg+V29DA\n\tvn6w8kfaMY8EUD/YBRsHXafvQacgGPXQELL7GzwxIZt9wpn7M7OoADpUjRxOKhO4IwJc\n\tTEzg==","X-Gm-Message-State":"AOJu0YzjPtTdmrcqwhJb/cZelVrLyNtzcEEJX539yx/yx2oddP1n200a\n\t4u4v2w16dWy9BkkoJctiNTeS+2a7rHU6qmyPzc3rYUpLdILcA9LNDV89TIlKq6T02rO08q+bw8e\n\ty+NF+yf/wTaOa9GF/4PH8uk2+E2RMIbFgmhl7fVPQ2K7qARpg3u0Na6zDcvoOs9g8S1XJ2EU/r9\n\tk=","X-Gm-Gg":"AR+sD10BloGxywbwf7YQw611BOUBg+aXwJL5quk61cu02FwQN2V0jSBdqFf9iaSTLHD\n\tseOfLfjUe4i47xCkXZ78XnnAOsS/NKy61DKhFcyRuwbfX32t96weOtIbTOCMTPBd5c19NYgOG9r\n\tQnlubtOnmtCK+oXig9e794CQ1uiwrH6Iu+DSKWWQPDEH1EkJH6upqyUaTBPvMCQo+G/C4jUuWgm\n\tOv8bsan38M50M6UeypQ4nH0kOMT7gB2jmb4gYcuJVD75UCq0daoffEJo6pihMPLtlLzcpEPHOOJ\n\tr13lBhY6pF04rz6NNXoymuoYsnYVTiZikcyOVHRkHZP/thTF5Ktbq+x9no2cV4cJI3WrwWWXI4b\n\trLtP7rjPCJJgerUhHWHAU+oLgqbpwJfKnQDhGeqKKO0Lg6dRjEC2qjg==","X-Received":["by 2002:a5d:5183:0:b0:47d:dfc6:b28b with SMTP id\n\tffacd0b85a97d-47fec62fc53mr12526445f8f.30.1785955766280; \n\tWed, 05 Aug 2026 11:49:26 -0700 (PDT)","by 2002:a5d:5183:0:b0:47d:dfc6:b28b with SMTP id\n\tffacd0b85a97d-47fec62fc53mr12526398f8f.30.1785955765731; \n\tWed, 05 Aug 2026 11:49:25 -0700 (PDT)"],"From":"Milan Zamazal <mzamazal@redhat.com>","To":"Jacopo Mondi <jacopo.mondi@ideasonboard.com>","Cc":"libcamera-devel@lists.libcamera.org,  Stefan Klug\n\t<stefan.klug@ideasonboard.com>,  Kieran Bingham\n\t<kieran.bingham@ideasonboard.com>,  Robert Mader\n\t<robert.mader@collabora.com>","Subject":"Re: [PATCH v7 03/32] ipa: simple: awb: Port to use libipa\n\tAwbAlgorithm","In-Reply-To":"<20260805-libipa-algorithms-v7-3-7425b5b795d4@ideasonboard.com>\n\t(Jacopo Mondi's message of \"Wed, 05 Aug 2026 18:12:45 +0200\")","References":"<20260805-libipa-algorithms-v7-0-7425b5b795d4@ideasonboard.com>\n\t<20260805-libipa-algorithms-v7-3-7425b5b795d4@ideasonboard.com>","Date":"Wed, 05 Aug 2026 20:49:23 +0200","Message-ID":"<85ecgc2yj0.fsf@mzamazal-thinkpadp1gen7.tpbc.csb>","User-Agent":"Gnus/5.13 (Gnus v5.13)","MIME-Version":"1.0","X-Mimecast-Spam-Score":"0","X-Mimecast-MFC-PROC-ID":"AAFfhGjj0fGUiVTVDOzmfFhGyxxwuKuLLQncvuPF0V4_1785955766","X-Mimecast-Originator":"redhat.com","Content-Type":"text/plain","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>"}}]