From patchwork Thu Jun 18 10:18:44 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jacopo Mondi X-Patchwork-Id: 26920 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id 3390CC3303 for ; Thu, 18 Jun 2026 10:19:06 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 24ABD6299E; Thu, 18 Jun 2026 12:19:01 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="O25FVRPY"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 04029629A2 for ; Thu, 18 Jun 2026 12:18:56 +0200 (CEST) Received: from [192.168.125.177] (mob-109-113-4-199.net.vodafone.it [109.113.4.199]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id C7CF82590; Thu, 18 Jun 2026 12:18:20 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1781777901; bh=Z7lE/WfKo0sHH63MuF6EmA1ItDT3yRfiduAdkaiijHw=; h=From:Date:Subject:References:In-Reply-To:To:Cc:From; b=O25FVRPYWixraPWtAU7PQTSIzQyl12WYJn+Iz4IYnokbDkOUhkjruAHtl+RoOTMv0 /cqjI/M7OOogumPDPtR0OA6CCTzPxX/LrUzUsPycKoH11Jf8dbT2ugD60Z1FfhQBNj rCKHLP/Rm09AY6htnWUp48ebC5T8V37KyiH6tLJ4= From: Jacopo Mondi Date: Thu, 18 Jun 2026 12:18:44 +0200 Subject: [PATCH 05/14] ipa: libipa: Add CcmAlgorithm to libipa MIME-Version: 1.0 Message-Id: <20260618-rppx1-ipa-v1-5-32337264cfcd@ideasonboard.com> References: <20260618-rppx1-ipa-v1-0-32337264cfcd@ideasonboard.com> In-Reply-To: <20260618-rppx1-ipa-v1-0-32337264cfcd@ideasonboard.com> To: =?utf-8?q?Niklas_S=C3=B6derlund?= , libcamera-devel@lists.libcamera.org Cc: Jacopo Mondi X-Mailer: b4 0.14.3 X-Developer-Signature: v=1; a=openpgp-sha256; l=12610; i=jacopo.mondi@ideasonboard.com; h=from:subject:message-id; bh=Z7lE/WfKo0sHH63MuF6EmA1ItDT3yRfiduAdkaiijHw=; b=owEBbQKS/ZANAwAKAXI0Bo8WoVY8AcsmYgBqM8YJ3o08FccxEMHSnNKxtFLICcYrfcDgHT8YH MBo0S0bnfeJAjMEAAEKAB0WIQS1xD1IgJogio9YOMByNAaPFqFWPAUCajPGCQAKCRByNAaPFqFW PKhwEACSwAgykIyqFT6qUs9BeS13tloqvx4zhClhMDSwZbW0j3D+ukLvR+tljoHslh/RsxXWeY0 l+qxSj30azunG5qYMr9hZrEJqHb7tXgJzQrJJXJD3muQOJGBHJSdyOxnVHZ95z05BQqgxu6c5rz SiCq8Uwhe3bD2a/x3EV+BAF5ospUs9GcaIdF4cDSC2GsS7e08VA/YLHgKyhEwRz1L6dd3sztLZS M5+Y853qS+pwd2OS9XROJ3Gs8HikszATmtisnZRJ7tdp3kbn/dAuu5Ppj1IwIgd2K7hynrfHW6/ 9jyqkv1sHkvtWDJ3GtVcbaqXW0cMlfPpjv7fxRI4mzVypGun7tzcwmKVzrcnDEpNI5NcL9krine d+X/HhjNxbWcHVQgu54VxzH6JSMRBfSV5sdG1ZgPQG/0Ogg1EFzPAOtOEumM+RBfnTDJlxmI6uY 7uNHu9lk71ylsytj7M4pk1ilixO7MF2FMnmyeCbHRKMAXnO9ZWZn1rLT7g9BCFOlRvejxioCGdW sDA7qes9p6MqaEI6i6e2PShT1Pnr7lnCWE/D+HjaTMXVjPqRGDF4mdC+i+rpEXSwXSnu3msvCE4 ThbJb5alFYAxlrbwfXTG08P607p3BhCX1auUvgQV3MPwKbtIx9gx9Hu//8KRvWHwvLOGpO/zAyf W7Vpb6HAA7YGcog== X-Developer-Key: i=jacopo.mondi@ideasonboard.com; a=openpgp; fpr=72392EDC88144A65C701EA9BA5826A2587AD026B X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" Add a CcmAlgorithm to libipa. The CcmAlgorithm performs interpolation of the colour correction matrices as loaded from tuning file on a colour temperature. The implementation is based on the existing RkISP1 CCM algorihtm. Signed-off-by: Jacopo Mondi --- src/ipa/libipa/ccm.cpp | 265 +++++++++++++++++++++++++++++++++++++++++++++ src/ipa/libipa/ccm.h | 84 ++++++++++++++ src/ipa/libipa/meson.build | 2 + 3 files changed, 351 insertions(+) diff --git a/src/ipa/libipa/ccm.cpp b/src/ipa/libipa/ccm.cpp new file mode 100644 index 000000000000..9fc8982e5a95 --- /dev/null +++ b/src/ipa/libipa/ccm.cpp @@ -0,0 +1,265 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026 Ideas on Board Oy + * + * libIPA CCM algorithm + */ + +#include "ccm.h" + +/** + * \file ccm.h + * \brief libipa ccm (Colour Correction Matrix) algorithm + */ + +namespace libcamera { + +namespace ipa { + +LOG_DEFINE_CATEGORY(Ccm) + +namespace ccm { + +/** + * \struct ActiveState + * \brief Active ccm state + * + * \var ActiveState::manual + * \brief The most recent manually requested ccm state + * + * \var ActiveState::automatic + * \brief The most recent automatically calculated ccm state + */ + +/** + * \struct CcmContext + * \brief Ccm coefficients and offsets + * + * \var CcmContext::ccm + * \brief Matrix of 3x3 ccm coefficients + * + * \var CcmContext::offsets + * \brief Vector of RGB ccm offsets + */ + +/** + * \typedef FrameContext + * \brief Per-frame ccm state + */ + +} /* namespace ccm */ + +/** + * \class CcmAlgorithmBase + * \brief Base class for CcmAlgorithm for non-templated functions implementation + * + * Base class for CcmAlgorithm where non-templated functions are implemented. + * IPA implementations shall use CcmAlgorithm and not this class. + */ + +/** + * \brief Initialize the algorithm with the given tuning data + * \param[in] tuningData The tuning data to use for the algorithm + * + * Parse \a tuningData to initialize the ccm algorithm and register controls. + * IPA modules are expected to call this function as part of their + * implementation of Algorithm::init(). + * + * \return 0 on success, a negative error code otherwise + */ +int CcmAlgorithmBase::init(const ValueNode &tuningData) +{ + int ret = ccm_.readYaml(tuningData["ccms"], "ct", "ccm"); + if (ret < 0) { + LOG(Ccm, Warning) + << "Failed to parse 'ccm' " + << "parameter from tuning file; falling back to unit matrix"; + ccm_.setData({ { 0, Matrix::identity() } }); + } + + ret = offsets_.readYaml(tuningData["ccms"], "ct", "offsets"); + if (ret < 0) { + LOG(Ccm, Warning) + << "Failed to parse 'offsets' " + << "parameter from tuning file; falling back to zero offsets"; + + offsets_.setData({ { 0, Matrix({ 0, 0, 0 }) } }); + } + + return 0; +} + +/** + * \brief Configure the ccm algorithm + * \param[in] state The ccm active state + * \param[in] temperatureK The colour temperature in Kelvin + * + * Configure the ccm algorithm by initializing the manual and automatic + * states in \a state by interpolating the default colour correction matrix + * with the given colour temperature \a temperatureK. + * + * \return 0 if successful, an error code otherwise + */ +int CcmAlgorithmBase::configure(ccm::ActiveState &state, unsigned int temperatureK) +{ + state.manual.ccm = ccm_.getInterpolated(temperatureK); + state.manual.offsets = offsets_.getInterpolated(temperatureK); + state.automatic.ccm = ccm_.getInterpolated(temperatureK); + state.automatic.offsets = offsets_.getInterpolated(temperatureK); + + return 0; +} + +/** + * \brief Queue a Request to the ccm algorithm + * \param[in] state The ccm active state + * \param[in] context The ccm frame context + * \param[in] controls The list of controls part of the Request + * + * Queue a new Request to the ccm algorithm and store the manual colour + * correction matrix and temperature in \a frameContext. + * + * The currently handled controls are: + * - controls::ColourTemperature + * - controls::ColourCorrectionMatrix + * + * When controls::ColourCorrectionMatrix is passed in the supplied matrix is + * stored in \a state and \a context. + * + * When controls::ColourTemperature is passed in, the matrices loaded from + * configuration file are interpolated with the given temperature and the result + * is stored in \a state and \a context. + * + * If the IPA is running in manual mode, the IPA ccm algorithm implementations + * can use the matrix coefficients and offsets directly from \a context after + * calling this function to program the HW ccm engine, without calling prepare(). + */ +void CcmAlgorithmBase::queueRequest(ccm::ActiveState &state, + ccm::FrameContext &context, + const ControlList &controls) +{ + const auto &colourTemperature = controls.get(controls::ColourTemperature); + const auto &ccmMatrix = controls.get(controls::ColourCorrectionMatrix); + if (ccmMatrix) { + state.manual.ccm = Matrix(*ccmMatrix); + LOG(Ccm, Debug) << "Setting manual CCM from CCM control to " + << state.manual.ccm; + } else if (colourTemperature) { + state.manual.ccm = ccm_.getInterpolated(*colourTemperature); + LOG(Ccm, Debug) << "Setting manual CCM from CT control to " + << state.manual.ccm; + } + + context = state.manual; +} + +/** + * \brief Calculate the matrix coefficients for a colour temperature + * \param[in] state The ccm active state + * \param[in] context The ccm frame context + * \param[in] frame The frame number + * \param[in] temperatureK The colour temperature in Kelvin + * + * Interpolate the colour correction matrices as loaded from configuration file + * for colour temperature \a temperatureK. + * + * The function shall only be called if the IPA algorithm is running in auto + * mode. If running in manual mode the application supplied correction matrix is + * stored in \a frameContext at queueRequest() time. + */ +void CcmAlgorithmBase::prepare(ccm::ActiveState &state, + ccm::FrameContext &context, + unsigned int frame, unsigned int temperatureK) +{ + if (frame > 0 && temperatureK == ct_) { + context = state.automatic; + return; + } + + ct_ = temperatureK; + context.ccm = ccm_.getInterpolated(ct_); + context.offsets = offsets_.getInterpolated(ct_); + + state.automatic = context; +} + +/** + * \brief Populate metadata with the latest correction matrix coefficients + * \param[in] context The ccm frame context + * \param[out] metadata The metadata list + */ +void CcmAlgorithmBase::process(ccm::FrameContext &context, ControlList &metadata) +{ + metadata.set(controls::ColourCorrectionMatrix, context.ccm.data()); +} + +/** + * \var CcmAlgorithmBase::coeffMin_ + * \brief The minimum supported coefficients value + * + * Minimum coefficient value used to clamp the ccm algorithm calculation results + * in the range supported by the platform ccm engine. + * + * The min and max gain values are initialized by CcmAlgorithm::init(). + */ + +/** + * \var CcmAlgorithmBase::coeffMax_ + * \brief The maximum supported coefficients value + * + * Maximum coefficient value used to clamp the ccm algorithm calculation results + * in the range supported by the platform ccm engine. + * + * The min and max gain values are initialized by CcmAlgorithm::init(). + */ + +/** + * \class CcmAlgorithm + * \brief The libipa ccm algorithm + * \tparam Q The fixedpoint register representation of the colour correction + * coefficients + * + * Implement the ccm algorithm for libipa. + * + * The CcmAlgorithm class implement an interface similar in spirit to the one + * of the Algorithm class. IPA modules are expected to store an instance of + * CcmAlgorithm as class member, template it with the ccm coefficients register + * representation and call its function in their implementations of the + * Algorithm interface. + * + * The CcmAlgorithm class provides an init() function where tuning data is + * parsed and the per-colour temperature correction matrices are loaded from + * the tuning file. + * + * CcmAlgorithm supports both automatic and manual colour correction operations, + * but doesn't offer a way to select one of them. Enabling or disabling + * automatic ccm operations usually goes through the Awb algorithm + * enable/disable as the two algorithms should work with the same mode. + * + * When the Awb algorithm runs in manual mode a custom colour correction matrix + * or a custom colour temperature can be supplied to the ccm algorithm at + * queueRequest() time. If the Request contains a color correction matrix + * (controls::ColourCorrectionMatrix) then the matrix coefficients gets saved in + * the FrameContext and the IPA module can immediately use them and doesn't need + * to call process(). If a custom colour temperature is provided + * (controls::ColourTemperature) then the matrices loaded from configuration are + * interpolated with it and the result is saved in the FrameContext. In this + * case as well IPA modules can use the result immediately and should avoid + * calling process(). + * + * When the Awb algorithm runs in automatic mode instead, it estimates the scene + * colour temperature. The estimated colour temperature shall be passed to + * process(), where it is used to interpolate the matrices loaded from the + * tuning file. The resulting coefficients are stored in the FrameContext for + * the IPA algorithm to use them to program their ccm engine registers. + */ + +/** + * \fn CcmAlgorithm::init() + * \param[in] controls The info map of the IPA controls + * \copydoc CcmAlgorithmBase::init() + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/ccm.h b/src/ipa/libipa/ccm.h new file mode 100644 index 000000000000..6bb519d20dda --- /dev/null +++ b/src/ipa/libipa/ccm.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2026 Ideas on Board Oy + * + * libIPA CCM algorithm + */ + +#pragma once + +#include +#include + +#include "libcamera/internal/matrix.h" + +#include "fixedpoint.h" +#include "interpolator.h" + +namespace libcamera { + +namespace ipa { + +namespace ccm { + +struct CcmContext { + Matrix ccm; + Matrix offsets; +}; + +struct ActiveState { + struct CcmContext manual; + struct CcmContext automatic; +}; + +using FrameContext = CcmContext; + +} /* namespace ccm */ + +class CcmAlgorithmBase +{ +public: + int init(const ValueNode &tuningData); + int configure(ccm::ActiveState &state, unsigned int temperatureK); + void queueRequest(ccm::ActiveState &state, ccm::FrameContext &context, + const ControlList &controls); + + void prepare(ccm::ActiveState &state, ccm::FrameContext &context, + unsigned int frame, unsigned int temperatureK); + void process(ccm::FrameContext &context, ControlList &metadata); + +protected: + float coeffMin_; + float coeffMax_; + +private: + unsigned int ct_; + Interpolator> ccm_; + Interpolator> offsets_; +}; + +template +class CcmAlgorithm : public CcmAlgorithmBase +{ +public: + int init(const ValueNode &tuningData, ControlInfoMap::Map &controls) + { + int ret = CcmAlgorithmBase::init(tuningData); + if (ret) + return ret; + + coeffMin_ = Q::TraitsType::min; + coeffMax_ = Q::TraitsType::max; + + controls[&controls::ColourCorrectionMatrix] = + ControlInfo(ControlValue(coeffMin_), + ControlValue(coeffMax_), + ControlValue(Matrix::identity().data())); + + return 0; + } +}; + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/meson.build b/src/ipa/libipa/meson.build index 963c5ee73063..edf8eabd8b78 100644 --- a/src/ipa/libipa/meson.build +++ b/src/ipa/libipa/meson.build @@ -7,6 +7,7 @@ libipa_headers = files([ 'awb_grey.h', 'awb.h', 'camera_sensor_helper.h', + 'ccm.h', 'colours.h', 'exposure_mode_helper.h', 'fc_queue.h', @@ -28,6 +29,7 @@ libipa_sources = files([ 'awb_grey.cpp', 'awb.cpp', 'camera_sensor_helper.cpp', + 'ccm.cpp', 'colours.cpp', 'exposure_mode_helper.cpp', 'fc_queue.cpp',