[{"id":35270,"web_url":"https://patchwork.libcamera.org/comment/35270/","msgid":"<CAHW6GYL8JPO_QBEy8igGmb+Mf8njCufRC--_X3+VZkXS0vLysg@mail.gmail.com>","date":"2025-08-04T12:52:27","subject":"Re: [PATCH] ipa: rpi: ccm: Implement \"manual\" CCM mode","submitter":{"id":42,"url":"https://patchwork.libcamera.org/api/people/42/","name":"David Plowman","email":"david.plowman@raspberrypi.com"},"content":"There's some stuff wrong here. Because our CCM algorithm is\nindependent of the AWB, the coupling between the two (AWB must be off\nfor a manual CCM) doesn't really work. It's not really what I would\nhave wanted, either (why can't you have a fixed CCM just because AWB\nis running?), but I suppose I can implement what is documented.\n\nI'm going to have to post a v2.\n\nDavid\n\nOn Mon, 4 Aug 2025 at 12:11, David Plowman\n<david.plowman@raspberrypi.com> wrote:\n>\n> The CCM algorithm will now let an explicit colour matrix be set when\n> AWB is in manual mode.\n>\n> Signed-off-by: David Plowman <david.plowman@raspberrypi.com>\n> ---\n>  src/ipa/rpi/common/ipa_base.cpp        | 67 +++++++++++++++++++++++---\n>  src/ipa/rpi/common/ipa_base.h          |  2 +\n>  src/ipa/rpi/controller/ccm_algorithm.h |  6 +++\n>  src/ipa/rpi/controller/rpi/ccm.cpp     | 21 +++++++-\n>  src/ipa/rpi/controller/rpi/ccm.h       |  5 +-\n>  5 files changed, 92 insertions(+), 9 deletions(-)\n>\n> diff --git a/src/ipa/rpi/common/ipa_base.cpp b/src/ipa/rpi/common/ipa_base.cpp\n> index ce2343e9..3153bee6 100644\n> --- a/src/ipa/rpi/common/ipa_base.cpp\n> +++ b/src/ipa/rpi/common/ipa_base.cpp\n> @@ -94,6 +94,7 @@ const ControlInfoMap::Map ipaColourControls{\n>         { &controls::AwbEnable, ControlInfo(false, true) },\n>         { &controls::AwbMode, ControlInfo(controls::AwbModeValues) },\n>         { &controls::ColourGains, ControlInfo(0.0f, 32.0f) },\n> +       { &controls::ColourCorrectionMatrix, ControlInfo(0.0f, 8.0f) },\n>         { &controls::ColourTemperature, ControlInfo(100, 100000) },\n>         { &controls::Saturation, ControlInfo(0.0f, 32.0f, 1.0f) },\n>  };\n> @@ -126,7 +127,7 @@ namespace ipa::RPi {\n>  IpaBase::IpaBase()\n>         : controller_(), frameLengths_(FrameLengthsQueueSize, 0s), statsMetadataOutput_(false),\n>           stitchSwapBuffers_(false), frameCount_(0), mistrustCount_(0), lastRunTimestamp_(0),\n> -         firstStart_(true), flickerState_({ 0, 0s })\n> +         firstStart_(true), flickerState_({ 0, 0s }), awbEnabled_(true)\n>  {\n>  }\n>\n> @@ -1049,13 +1050,21 @@ void IpaBase::applyControls(const ControlList &controls)\n>                                 break;\n>                         }\n>\n> -                       if (ctrl.second.get<bool>() == false)\n> +                       awbEnabled_ = ctrl.second.get<bool>();\n> +\n> +                       if (!awbEnabled_)\n>                                 awb->disableAuto();\n> -                       else\n> +                       else {\n>                                 awb->enableAuto();\n>\n> -                       libcameraMetadata_.set(controls::AwbEnable,\n> -                                              ctrl.second.get<bool>());\n> +                               /* If AWB is running, the CCM must go back to auto as well. */\n> +                               RPiController::CcmAlgorithm *ccm = dynamic_cast<RPiController::CcmAlgorithm *>(\n> +                                       controller_.getAlgorithm(\"ccm\"));\n> +                               if (ccm)\n> +                                       ccm->enableAuto();\n> +                       }\n> +\n> +                       libcameraMetadata_.set(controls::AwbEnable, awbEnabled_);\n>                         break;\n>                 }\n>\n> @@ -1098,10 +1107,20 @@ void IpaBase::applyControls(const ControlList &controls)\n>                         }\n>\n>                         awb->setManualGains(gains[0], gains[1]);\n> -                       if (gains[0] != 0.0f && gains[1] != 0.0f)\n> +                       if (gains[0] != 0.0f && gains[1] != 0.0f) {\n>                                 /* A gain of 0.0f will switch back to auto mode. */\n>                                 libcameraMetadata_.set(controls::ColourGains,\n>                                                        { gains[0], gains[1] });\n> +                               awbEnabled_ = false; /* puts AWB into manual mode */\n> +                       } else {\n> +                               awbEnabled_ = true; /* puts AWB into auto mode */\n> +\n> +                               /* If AWB is running, the CCM must go back to auto as well. */\n> +                               RPiController::CcmAlgorithm *ccm = dynamic_cast<RPiController::CcmAlgorithm *>(\n> +                                       controller_.getAlgorithm(\"ccm\"));\n> +                               if (ccm)\n> +                                       ccm->enableAuto();\n> +                       }\n>                         break;\n>                 }\n>\n> @@ -1120,10 +1139,46 @@ void IpaBase::applyControls(const ControlList &controls)\n>                         }\n>\n>                         awb->setColourTemperature(temperatureK);\n> +                       awbEnabled_ = false; /* this puts AWB into manual mode */\n>                         /* This metadata will get reported back automatically. */\n>                         break;\n>                 }\n>\n> +               case controls::COLOUR_CORRECTION_MATRIX: {\n> +                       if (monoSensor_)\n> +                               break;\n> +\n> +                       auto floats = ctrl.second.get<Span<const float>>();\n> +                       RPiController::CcmAlgorithm *ccm = dynamic_cast<RPiController::CcmAlgorithm *>(\n> +                               controller_.getAlgorithm(\"ccm\"));\n> +                       if (!ccm) {\n> +                               LOG(IPARPI, Warning)\n> +                                       << \"Could not set COLOUR_CORRECTION_MATRIX - no CCM algorithm\";\n> +                               break;\n> +                       }\n> +\n> +                       /* We are guaranteed this control contains 9 values. Nevertheless: */\n> +                       assert(floats.size() == 9);\n> +\n> +                       Matrix<double, 3, 3> matrix;\n> +                       for (std::size_t i = 0; i < 3; ++i)\n> +                               for (std::size_t j = 0; j < 3; ++j)\n> +                                       matrix[i][j] = static_cast<double>(floats[i * 3 + j]);\n> +\n> +                       ccm->setCcm(matrix);\n> +\n> +                       /*\n> +                        * But if AWB is running, go back to auto mode. The CCM gets remembered,\n> +                        * which avoids the race between setting the CCM and disabling AWB in\n> +                        * the same set of controls.\n> +                        */\n> +                       if (awbEnabled_)\n> +                               ccm->enableAuto();\n> +\n> +                       /* This metadata will be reported back automatically. */\n> +                       break;\n> +               }\n> +\n>                 case controls::BRIGHTNESS: {\n>                         RPiController::ContrastAlgorithm *contrast = dynamic_cast<RPiController::ContrastAlgorithm *>(\n>                                 controller_.getAlgorithm(\"contrast\"));\n> diff --git a/src/ipa/rpi/common/ipa_base.h b/src/ipa/rpi/common/ipa_base.h\n> index e2f6e330..5348f2ea 100644\n> --- a/src/ipa/rpi/common/ipa_base.h\n> +++ b/src/ipa/rpi/common/ipa_base.h\n> @@ -140,6 +140,8 @@ private:\n>                 int32_t mode;\n>                 utils::Duration manualPeriod;\n>         } flickerState_;\n> +\n> +       bool awbEnabled_;\n>  };\n>\n>  } /* namespace ipa::RPi */\n> diff --git a/src/ipa/rpi/controller/ccm_algorithm.h b/src/ipa/rpi/controller/ccm_algorithm.h\n> index 6678ba75..785c408a 100644\n> --- a/src/ipa/rpi/controller/ccm_algorithm.h\n> +++ b/src/ipa/rpi/controller/ccm_algorithm.h\n> @@ -6,16 +6,22 @@\n>   */\n>  #pragma once\n>\n> +#include \"libcamera/internal/matrix.h\"\n> +\n>  #include \"algorithm.h\"\n>\n>  namespace RPiController {\n>\n> +using Matrix3x3 = libcamera::Matrix<double, 3, 3>;\n> +\n>  class CcmAlgorithm : public Algorithm\n>  {\n>  public:\n>         CcmAlgorithm(Controller *controller) : Algorithm(controller) {}\n>         /* A CCM algorithm must provide the following: */\n> +       virtual void enableAuto() = 0;\n>         virtual void setSaturation(double saturation) = 0;\n> +       virtual void setCcm(Matrix3x3 const &matrix) = 0;\n>  };\n>\n>  } /* namespace RPiController */\n> diff --git a/src/ipa/rpi/controller/rpi/ccm.cpp b/src/ipa/rpi/controller/rpi/ccm.cpp\n> index 8607f152..25bcea2d 100644\n> --- a/src/ipa/rpi/controller/rpi/ccm.cpp\n> +++ b/src/ipa/rpi/controller/rpi/ccm.cpp\n> @@ -32,7 +32,7 @@ LOG_DEFINE_CATEGORY(RPiCcm)\n>  using Matrix3x3 = Matrix<double, 3, 3>;\n>\n>  Ccm::Ccm(Controller *controller)\n> -       : CcmAlgorithm(controller), saturation_(1.0) {}\n> +       : CcmAlgorithm(controller), enableAuto_(true), saturation_(1.0) {}\n>\n>  char const *Ccm::name() const\n>  {\n> @@ -78,11 +78,22 @@ int Ccm::read(const libcamera::YamlObject &params)\n>         return 0;\n>  }\n>\n> +void Ccm::enableAuto()\n> +{\n> +       enableAuto_ = true;\n> +}\n> +\n>  void Ccm::setSaturation(double saturation)\n>  {\n>         saturation_ = saturation;\n>  }\n>\n> +void Ccm::setCcm(Matrix3x3 const &matrix)\n> +{\n> +       enableAuto_ = false;\n> +       manualCcm_ = matrix;\n> +}\n> +\n>  void Ccm::initialise()\n>  {\n>  }\n> @@ -151,7 +162,13 @@ void Ccm::prepare(Metadata *imageMetadata)\n>                 LOG(RPiCcm, Warning) << \"no colour temperature found\";\n>         if (!luxOk)\n>                 LOG(RPiCcm, Warning) << \"no lux value found\";\n> -       Matrix3x3 ccm = calculateCcm(config_.ccms, awb.temperatureK);\n> +\n> +       Matrix3x3 ccm;\n> +       if (enableAuto_)\n> +               ccm = calculateCcm(config_.ccms, awb.temperatureK);\n> +       else\n> +               ccm = manualCcm_;\n> +\n>         double saturation = saturation_;\n>         struct CcmStatus ccmStatus;\n>         ccmStatus.saturation = saturation;\n> diff --git a/src/ipa/rpi/controller/rpi/ccm.h b/src/ipa/rpi/controller/rpi/ccm.h\n> index c05dbb17..70f28ed3 100644\n> --- a/src/ipa/rpi/controller/rpi/ccm.h\n> +++ b/src/ipa/rpi/controller/rpi/ccm.h\n> @@ -8,7 +8,6 @@\n>\n>  #include <vector>\n>\n> -#include \"libcamera/internal/matrix.h\"\n>  #include <libipa/pwl.h>\n>\n>  #include \"../ccm_algorithm.h\"\n> @@ -33,13 +32,17 @@ public:\n>         Ccm(Controller *controller = NULL);\n>         char const *name() const override;\n>         int read(const libcamera::YamlObject &params) override;\n> +       void enableAuto() override;\n>         void setSaturation(double saturation) override;\n> +       void setCcm(Matrix3x3 const &matrix) override;\n>         void initialise() override;\n>         void prepare(Metadata *imageMetadata) override;\n>\n>  private:\n>         CcmConfig config_;\n> +       bool enableAuto_;\n>         double saturation_;\n> +       Matrix3x3 manualCcm_;\n>  };\n>\n>  } /* namespace RPiController */\n> --\n> 2.39.5\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 1F6CBBE086\n\tfor <parsemail@patchwork.libcamera.org>;\n\tMon,  4 Aug 2025 12:52:45 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id 1EE4B69217;\n\tMon,  4 Aug 2025 14:52:44 +0200 (CEST)","from mail-qv1-xf35.google.com (mail-qv1-xf35.google.com\n\t[IPv6:2607:f8b0:4864:20::f35])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id A9B1C6920D\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon,  4 Aug 2025 14:52:41 +0200 (CEST)","by mail-qv1-xf35.google.com with SMTP id\n\t6a1803df08f44-70756dc2c00so50024676d6.1\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tMon, 04 Aug 2025 05:52:41 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com; dkim=pass (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"bpE/ajYG\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google; t=1754311960; x=1754916760;\n\tdarn=lists.libcamera.org; \n\th=to:subject:message-id:date:from:in-reply-to:references:mime-version\n\t:from:to:cc:subject:date:message-id:reply-to;\n\tbh=JgWtxI5p4yV9AhmmC60j7T7SSP8K927MrtWC4vUoNnU=;\n\tb=bpE/ajYG6u2flgv/aQsYx0s49Cg+xGQqsGgmBOoq0J4u67RTnuT93i9ENHfUpUwdwz\n\tGMAjczu0J5rQu4d8O4+TELQkxuQTEjQfbWHAcbbfyr5hpP7dCaRi4eDXbFEGIujPJ4BP\n\tM01cdrDmJmlTCBZVu6MbecY7hMhRIXhjZVw7Bowzxdg1PQaOwsmNKxpdkF7VmWufbiqr\n\t1FtYIN0FcF7mfisT3H5xgsOMS++AvNaora6iSMYkq98WnFci0C8VkYgDbr/3VrogNCEG\n\tg/LPUOB1C3raFq3MAzmKRP938SC2KcBcfLKaT678U4EG6Ao408G1iV9nm2zjtqV/BZxR\n\tgxWA==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20230601; t=1754311960; x=1754916760;\n\th=to:subject:message-id:date:from:in-reply-to:references:mime-version\n\t:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to;\n\tbh=JgWtxI5p4yV9AhmmC60j7T7SSP8K927MrtWC4vUoNnU=;\n\tb=aCtMYfOAdRZg4xLeuorVzC37SoK28lIEtYrCNNTl3EfS+euU6DQDyklcCd7eJBUO2u\n\taJtxbTzBHClhE3E1Tubwbc0+g86cMIO4A5DGpCz0nGYBl9noOxysmRb/Hkhe4Obke/Pi\n\tQxGv6Y2Ma/mREnL0TkB2R3vryzHTfMWWIwUjIfFOiBoCjvBCJFwhHnjwvfUTBU0E0MZC\n\tE0wm5OF7wPJG1OhKr7RIm0iujLRRvdzBAvfXzhkG58xXlTgHitOv+JOSAdtBb5AejF4u\n\tbAu+e1HMz+svorxcjsoK0dBpwekTSBFacnFKpb6QDmJCpuNvTer4LKTPqUqv0+r5pKvk\n\tnN2A==","X-Gm-Message-State":"AOJu0YzPsENqXgdXwDDUyzk5G2+dx7O9ddSwuVaeBGSjSfZifh/n//Ic\n\tIW2E65s7zNa6n4YGyrM8Mb6Q9SQjPI348rVq8+J+rqT9jPD2V0on+xaCFtLtWHv5HE0Z9VnThlK\n\tq0BwUlLZizijQV/PFX55HNpWYSdyeKQDY+z0P93UQ1ULeHDjpmLajHzQ=","X-Gm-Gg":"ASbGncs/IdnPOxFBovuOYj8AXjy+PqJZ2ezVOYQeAieV1nSDW7GFbPhb4zRckY3Wnd9\n\tpUP5pwYvCzgCua3FyxiyxSoW0/ClB0qftdUQDCi+nsMbTRz1WXBsVupJpzcobH+nufDMks5K7tp\n\tX+l39YnKqkL0vo2XeW9W7cnYGO5Iy/BdxO0CNlLIha3LbNi2y3JOtvUMZEg76TVegbFj1J41CQh\n\t5RfX7f2x5SVvjYtH/09l3R48HgIsXVBePS6P+gkGyhxt6CV","X-Google-Smtp-Source":"AGHT+IH+gpphFpjRSIPh7FFa1K1U5mE9twll6s4Vs2TLoyMqPuHmmXMG8BlU9f9B+KjXPplqzFpWwkyexgJQmsb/Sf4=","X-Received":"by 2002:a05:6214:3490:b0:709:538c:9ecf with SMTP id\n\t6a1803df08f44-709538c9f0bmr59155056d6.42.1754311959978;\n\tMon, 04 Aug 2025 05:52:39 -0700 (PDT)","MIME-Version":"1.0","References":"<20250804111118.10190-1-david.plowman@raspberrypi.com>","In-Reply-To":"<20250804111118.10190-1-david.plowman@raspberrypi.com>","From":"David Plowman <david.plowman@raspberrypi.com>","Date":"Mon, 4 Aug 2025 13:52:27 +0100","X-Gm-Features":"Ac12FXx63-OyrOSk08p1DjmWudTJS54qg4GEloLbniOF_TAeIEbtUqtM5lC9ktg","Message-ID":"<CAHW6GYL8JPO_QBEy8igGmb+Mf8njCufRC--_X3+VZkXS0vLysg@mail.gmail.com>","Subject":"Re: [PATCH] ipa: rpi: ccm: Implement \"manual\" CCM mode","To":"libcamera-devel@lists.libcamera.org","Content-Type":"text/plain; charset=\"UTF-8\"","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>"}}]